HTLC Dutch Auction Escrow API
Technical reference for the HTLC Dutch Auction escrow smart contract covering payloads, phases, validation rules, and error codes.
Overview
HTLC Dutch is a hybrid time-locked escrow with Dutch auction mechanics. It combines:
HTLC (Hash Time-Locked Contract): Time-based locks with a designated resolver.
Dutch Auction: Price decreases linearly over time after the resolver timeout.
Lock Operation
When to Use
Create a new escrow with tokens locked for a potential swap via designated resolver or Dutch auction.
Entry Point
Send jettons to the Minter contract with MinterLockPayload
as the forward payload.
Tolk Structure
struct (0x3e246684) MinterLockPayload {
quoteId: uint256,
refundToVault: bool,
refFee: address,
refFeeTier: uint16,
excesses: address,
userSafeDeposit: coins,
unlockData: cell,
unlockCondition: cell,
extraFields: Cell<MinterLockPayloadExtraFields>
}
struct MinterLockPayloadExtraFields {
ownerPubkey: Cell<uint256>?,
userUnlockForwardParams: Cell<ForwardParams>?,
lockForwardParams: Cell<LockForwardParams>?
}
struct ForwardParams {
value: coins,
successPayload: cell?,
rejectPayload: cell?
}
struct LockForwardParams {
successDest: address,
forwardParams: ForwardParams
}
struct HtlcDutchLockParams {
initOfferAmount: coins,
askJettonWallet: address,
askJettonMinter: address,
resolverData: Cell<ResolverData>,
dutchData: Cell<DutchData>
}
struct ResolverData {
resolver: address,
resolverTimeout: uint64, // seconds, converted to absolute timestamp
resolverAskAmount: coins
}
struct DutchData {
dutchRemaining: coins,
dutchMinAskAmount: coins,
dutchTimeout: uint64, // seconds, added to resolverTimeout for absolute timestamp
dutchResolverPublicKey: uint256?
}
TLB Schema
minter_lock_payload#3E246684
quoteId:uint256
refundToVault:Bool
refFee:MsgAddress
refFeeTier:uint16
excesses:MsgAddress
userSafeDeposit:Grams
unlockData:^Cell
unlockCondition:^Cell
extraFields:^MinterLockPayloadExtraFields
= MinterLockPayload;
minter_lock_payload_extra_fields#_
ownerPubkey:(Maybe ^uint256)
userUnlockForwardParams:(Maybe ^ForwardParams)
lockForwardParams:(Maybe ^LockForwardParams)
= MinterLockPayloadExtraFields;
forward_params#_
value:Grams
successPayload:(Maybe ^Cell)
rejectPayload:(Maybe ^Cell)
= ForwardParams;
lock_forward_params#_
successDest:MsgAddress
forwardParams:ForwardParams
= LockForwardParams;
htlc_dutch_lock_params#_
initOfferAmount:Grams
askJettonWallet:MsgAddress
askJettonMinter:MsgAddress
resolverData:^ResolverData
dutchData:^DutchData
= HtlcDutchLockParams;
resolver_data#_
resolver:MsgAddress
resolverTimeout:uint64
resolverAskAmount:Grams
= ResolverData;
dutch_data#_
dutchRemaining:Grams
dutchMinAskAmount:Grams
dutchTimeout:uint64
dutchResolverPublicKey:(Maybe uint256)
= DutchData;
Field Descriptions
MinterLockPayload
quoteId
: Unique identifier for this escrow order.refundToVault
: Whether to refund tokens to the vault (true
) or directly to the user (false
) on failure.refFee
: Address receiving the referral fee.refFeeTier
: Referral fee tier (basis points).excesses
: Address to receive excess TON from gas refunds.userSafeDeposit
: Minimum TON deposit from the user for gas coverage.unlockData
: SerializedHtlcDutchLockParams
.unlockCondition
: Contract code that defines unlock logic. For HTLC Dutch, use hex:b5ee9c72410208010002c90003f6228210066ac100bae3022282107fd9a46ebae3026c21d0fa00fa40fa40d4d74c01d001d0f82302fa40d33f31fa003002fa0031fa00d33f31d3000193d70bff92306de202c8ce24cf0b3f5003fa02c9c827fa025003fa0213cb3f226e946c12cf8195cf8312cbffe2c9c85005fa0213cececcccc9817a106f00126d01030701fe326f15d72c23d5830c0cf2bfd33f31fa4031fa4031fa003001d0fa0031fa40fa40d4d74c01d001d001fa40d33ffa00308200cb2122c300f2f48200cb2221c300f2f403fa0031fa00d33fd3000193d70bff92306de28200cb235336b9f2f48200cb2428fa4430c000f2f48200cb2525fa4430c000f2f4f8235304a006c8ce16020076cb3f5006fa02c95043a05003a0c827fa025003fa0212cb3f226e946c12cf8195cf8312cbffe2c9c85005fa0213cececcccc9817a106f00126d6f0402d66c12d0fa00fa40fa40d4d74c01d001d0056f15d72c234b7d16c4f2bfd34131fa40fa40fa00fa40d74cd08200cb265148c70514f2f48200cb2728c300f2f48200cb2824fa4430c000f2f4f82305fa40d33ffa00300bfa00fa00d33fd3000193d70bff92306de253a4bbe30f0405009e3337388200cb295373c705f2f42b8200cb2a06ba15f2f401c8cecb3f5009fa02c9c8cf84205005fa0212cb3f216e9331cf8194cf83cbffe2c9c826fa0215ce13cecc12ccc959817a10046f036d6f0401fe29fa443109fa00fa00d30001948308d71992306de2236e92303a8e178200cb2b216eb397541cc4f910c30093313b70e21bf2f4e28200cb2c53c3b9f2f4532ba15610a851c6a124a81ca05325a1a904547e80a9845305bc8e10305454015610a98419a1520abc39083094102a395be25126a18200cb2d21c2fff2f48200cb2e06008451a7bb1af2f403c8ce12cb3f500bfa02c9c85007fa0201fa0219cb3f226e946c12cf8195cf8312cbffe2c9c85007fa0215ce13cecc13ccc902817a10046f036d6f0400046f047ee39484
extraFields
: Additional fields.
MinterLockPayloadExtraFields
ownerPubkey
: Optional public key for external cancel authorization.userUnlockForwardParams
: Optional forward parameters when the user receives a refund.lockForwardParams
: Optional forward parameters for lock success notification.
ForwardParams
value
: TON amount to attach to the forward message.successPayload
: Optional payload sent on successful operation.rejectPayload
: Optional payload sent on operation rejection.
LockForwardParams
successDest
: Address to receive lock success notification.forwardParams
: Forward parameters for the notification message.
HtlcDutchLockParams
initOfferAmount
: Total amount of offer tokens being locked.askJettonWallet
: Jetton wallet address that must receive ask jettons during unlock.askJettonMinter
: Minter address of the ask jetton (for verification).resolverData
: Serialized resolver configuration.dutchData
: Serialized Dutch auction configuration.
ResolverData
resolver
: Address authorized to fill the order at a fixed price.resolverTimeout
: Duration (seconds) of exclusive resolver period, stored as absolute timestamp after lock.resolverAskAmount
: Fixed price the resolver must pay (in ask jetton).
DutchData
dutchRemaining
: Amount of offer tokens still available (initially equalsinitOfferAmount
).dutchMinAskAmount
: Minimum price at auction end (floor price).dutchTimeout
: Duration (seconds) for the Dutch auction after the resolver period, stored as absolute timestamp.dutchResolverPublicKey
: Optional public key for signature verification during the Dutch phase.
Validation Rules
resolverTimeout
must be > 0.resolverAskAmount
must be > 0.dutchMinAskAmount
must be <resolverAskAmount
.askJettonWallet
must be in basechain (workchain 0).resolver
must be in basechain (workchain 0).
State After Lock
Contract stores HtlcDutchLockParams
with absolute timestamps calculated as:
resolverTimeout = now + input.resolverTimeout
dutchTimeout = now + input.resolverTimeout + input.dutchTimeout
Unlock Operation
When to Use
Execute the swap by sending ask jettons to receive offer tokens. Behavior depends on the current time.
Entry Point
Send jettons to the Minter contract with MinterUnlockPayload
as the forward payload.
Tolk Structure
struct (0x6a58f85c) MinterUnlockPayload {
quoteId: uint256,
resolverFillToVault: bool,
resolverRefundToVault: bool,
excesses: address,
unlockArgs: cell,
forwardParams: Cell<ForwardParams>?
}
struct HtlcDutchTryUnlockArgs {
minOut: coins, // minimum acceptable offer tokens
dutchIgnoreRefundAmount: coins, // skip refund if overpayment < this
dutchSignature: bits512? // signature from dutchResolverPublicKey
}
TLB Schema
minter_unlock_payload#6A58F85C
quoteId:uint256
resolverFillToVault:Bool
resolverRefundToVault:Bool
excesses:MsgAddress
unlockArgs:^Cell
forwardParams:(Maybe ^ForwardParams)
= MinterUnlockPayload;
htlc_dutch_try_unlock_args#_
minOut:Grams
dutchIgnoreRefundAmount:Grams
dutchSignature:(Maybe bits512)
= HtlcDutchTryUnlockArgs;
Field Descriptions
MinterUnlockPayload
quoteId
: Must match the locked escrow'squoteId
.resolverFillToVault
: Whether resolver's received tokens go to the vault (true
) or direct (false
).resolverRefundToVault
: Whether overpayment refund goes to the vault (true
) or direct (false
).excesses
: Address to receive excess TON and jetton refunds.unlockArgs
: SerializedHtlcDutchTryUnlockArgs
.forwardParams
: Optional forward parameters for notifications.
HtlcDutchTryUnlockArgs
minOut
: Slippage protection – minimum offer tokens to receive; reverts iftoReceive < minOut
.dutchIgnoreRefundAmount
: Gas optimization – skip refund if overpayment is below this threshold.dutchSignature
: Required signature ifdutchResolverPublicKey
is set; verified against the resolver address hash.
Unlock Phases
Phase 1: Resolver Period (now ≤ resolverTimeout
)
now ≤ resolverTimeout
)Behavior
Only the designated resolver can unlock.
Must send exactly
resolverAskAmount
of ask jettons.Receives the entire
initOfferAmount
of offer tokens.Sets
dutchRemaining
to 0 (fully filled).
Validations
msg.resolver == resolverData.resolver
msg.resolverSentAmount == resolverAskAmount
Phase 2: Dutch Auction (resolverTimeout < now < dutchTimeout
)
resolverTimeout < now < dutchTimeout
)Behavior
Anyone can unlock (subject to signature if required).
Price decreases linearly:
dutchPrice = (resolverAskAmount * (dutchTimeout - now) + dutchMinAskAmount * (now - resolverTimeout)) / (dutchTimeout - resolverTimeout)
Partial fills allowed – multiple unlocks can occur until
dutchRemaining == 0
.Overpayment is refunded if it exceeds
dutchIgnoreRefundAmount
.
Calculation
toReceive = floor(initOfferAmount * resolverSentAmount / dutchPrice)
If
toReceive > dutchRemaining
, cap at remaining and refund excess ask jettons.Update:
dutchRemaining -= toReceive
Validations
If
dutchResolverPublicKey
is set, verifydutchSignature
against resolver address hash.now < dutchTimeout
dutchRemaining - toReceive >= 0
minOut <= toReceive
Phase 3: Expired (now ≥ dutchTimeout
)
now ≥ dutchTimeout
)Order cannot be unlocked. User must call cancel to refund tokens.
Common Validations
resolverSentJettonWallet
must equalaskJettonWallet
.initOfferAmount
must be > 0 (should never fail unless state is corrupted).Unlock resolver address must be in basechain (workchain 0).
Cancel Operation
When to Use
Refund locked tokens to the original user. Typically after the Dutch auction expires or by user request.
Behavior
Sets both timeouts to the current time (making the order immediately expired).
Resets
dutchRemaining
toinitOfferAmount
(unfills any partial fills).Does not transfer tokens – handled by the Item contract after cancel succeeds.
Can be called by the owner at any time.
Effect
Subsequent unlock attempts fail with VM_EXIT_STATUS_DUTCH_TIMEOUT_EXPIRED
.
Error Codes
VM_EXIT_STATUS_RESOLVER_TIMEOUT_IS_ZERO
Resolver timeout zero
Lock failed: resolverTimeout
must be > 0
VM_EXIT_STATUS_RESOLVER_ASK_AMOUNT_IS_ZERO
Resolver ask amount zero
Lock failed: resolverAskAmount
must be > 0
VM_EXIT_STATUS_DUTCH_MIN_ASK_NOT_LESS_THAN_RESOLVER_ASK
Invalid price range
Lock failed: dutchMinAskAmount
must be < resolverAskAmount
VM_EXIT_STATUS_ASK_JETTON_WALLET_WRONG_WORKCHAIN
Invalid ask wallet workchain
Lock failed: askJettonWallet
must be in basechain
VM_EXIT_STATUS_RESOLVER_WRONG_WORKCHAIN
Invalid resolver workchain
Lock failed: resolver address must be in basechain
VM_EXIT_STATUS_WRONG_RESOLVER_SENT_JETTON_WALLET
Wrong jetton
Unlock failed: sent wrong jetton type
VM_EXIT_STATUS_INIT_OFFER_AMOUNT_IS_ZERO
Offer amount zero
Unlock failed: initOfferAmount
is zero (corrupted state)
VM_EXIT_STATUS_UNLOCK_RESOLVER_WRONG_WORKCHAIN
Invalid unlock resolver workchain
Unlock failed: resolver address must be in basechain
VM_EXIT_STATUS_RESOLVER_MISMATCH
Unauthorized resolver
Unlock failed during resolver period: wrong resolver address
VM_EXIT_STATUS_RESOLVER_ASK_AMOUNT_MISMATCH
Wrong amount
Unlock failed during resolver period: must send exact resolverAskAmount
VM_EXIT_STATUS_INVALID_SIGNATURE
Invalid signature
Unlock failed during Dutch: signature verification failed
VM_EXIT_STATUS_DUTCH_TIMEOUT_EXPIRED
Auction expired
Unlock failed: auction ended, use cancel
VM_EXIT_STATUS_DUTCH_REMAINING_NEGATIVE
Negative remaining
Unlock failed during Dutch: arithmetic error, remaining would be negative
VM_EXIT_STATUS_MIN_OUT_EXCEEDS_TO_RECEIVE
Slippage exceeded
Unlock failed: received less than minOut
Usage Examples
Example 1: Lock 100 STON for 50 USDT (Resolver) or 30–50 USDT (Dutch)
// Lock parameters
const lockParams: HtlcDutchLockParams = {
initOfferAmount: 100_000000n, // 100 STON
askJettonWallet: usdtWalletAddress,
askJettonMinter: usdtMinterAddress,
resolverData: {
resolver: trustedResolverAddress,
resolverTimeout: 3600n, // 1 hour exclusive
resolverAskAmount: 50_000000n, // 50 USDT fixed price
},
dutchData: {
dutchRemaining: 100_000000n,
dutchMinAskAmount: 30_000000n, // 30 USDT floor
dutchTimeout: 7200n, // 2 hours Dutch after resolver period
dutchResolverPublicKey: null, // anyone can fill during Dutch
},
};
// Send STON with MinterLockPayload
Example 2: Unlock During Dutch Auction
// At 4500 seconds after lock (1.5 hours = mid-Dutch)
// Price is 40 USDT (linear interpolation between 50 and 30)
// Sending 20 USDT gets: floor(100 * 20 / 40) = 50 STON
const unlockArgs: HtlcDutchTryUnlockArgs = {
minOut: 48_000000n, // accept >= 48 STON (slippage tolerance)
dutchIgnoreRefundAmount: 100000n, // skip refund if < 0.1 USDT overpaid
dutchSignature: null, // no signature required
};
// Send 20 USDT with MinterUnlockPayload
// Receive 50 STON, dutchRemaining becomes 50 STON
Example 3: Resolver Fills Entire Order
// Within first hour
const unlockArgs: HtlcDutchTryUnlockArgs = {
minOut: 0n,
dutchIgnoreRefundAmount: 0n,
dutchSignature: null,
};
// Resolver sends exactly 50 USDT
// Receives full 100 STON, dutchRemaining becomes 0
Related Resources
Contact the STON.fi team for partner onboarding or integration support.
Last updated