From 4eba166b27d00476d93f16edba8ab372acf55bdb Mon Sep 17 00:00:00 2001 From: Kristijan Date: Thu, 30 Jul 2026 11:36:34 +0200 Subject: [PATCH 01/11] Add TP <> ContextExecutor integration to support async flow continuation --- .../ccip/executors/context_executor/lib.tolk | 9 +- .../executors/context_executor/types.tolk | 28 +- .../ccip/pools/burn_mint/contract.tolk | 282 +++++++--- .../ccip/pools/burn_mint/errors.tolk | 8 +- .../ccip/pools/burn_mint/messages.tolk | 8 +- .../ccip/pools/burn_mint/storage.tolk | 20 +- .../contracts/ccip/pools/burn_mint/types.tolk | 19 +- .../ccip/pools/lock_release/contract.tolk | 170 +++--- .../ccip/pools/lock_release/errors.tolk | 5 +- .../ccip/pools/lock_release/messages.tolk | 4 +- .../ccip/pools/lock_release/storage.tolk | 16 +- .../ccip/pools/lock_release/types.tolk | 10 +- .../pools/lock_release_lockbox/contract.tolk | 7 +- contracts/tests/ccip/ContextExecutor.spec.ts | 81 +++ .../ccip/pools/BurnMintTokenPool.spec.ts | 101 ++-- .../pools/LockReleaseLockboxTokenPool.spec.ts | 19 +- .../ccip/pools/LockReleaseTokenPool.spec.ts | 194 +++++-- .../tests/ccip/pools/TokenPool.behavior.ts | 173 ++++++- .../wrappers/gen/ccip/ContextExecutor.ts | 2 +- .../gen/ccip/pools/BurnMintTokenPool.ts | 490 +++++++++++------- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 78 +-- .../gen/ccip/pools/LockReleaseTokenPool.ts | 311 +++++++---- 22 files changed, 1443 insertions(+), 592 deletions(-) create mode 100644 contracts/tests/ccip/ContextExecutor.spec.ts diff --git a/contracts/contracts/ccip/executors/context_executor/lib.tolk b/contracts/contracts/ccip/executors/context_executor/lib.tolk index c26841483..ebbe9b107 100644 --- a/contracts/contracts/ccip/executors/context_executor/lib.tolk +++ b/contracts/contracts/ccip/executors/context_executor/lib.tolk @@ -5,10 +5,6 @@ import "../../../lib/utils" import "types" import "messages" -struct ContextExecutor { - data: ContextExecutor_Data; -} - fun ContextExecutor.onInternalMessage( mutate self, in: ContextExecutor_InMessageForward, @@ -62,6 +58,9 @@ fun ContextExecutor.onInternalMessage( } else => { if (self._isForwardSender(in.senderAddress)) { + val forwardFrom = self.data.forwardFrom; + self.data.forwardFrom = []; + val reply = createMessage({ bounce: BounceMode.NoBounce, value: 0, @@ -69,7 +68,7 @@ fun ContextExecutor.onInternalMessage( body: ContextExecutor_ForwardNotification { id: self.data.id, context: self.data.context, - forwardFrom: self.data.forwardFrom, + forwardFrom, message: in.toCell(), }, }); diff --git a/contracts/contracts/ccip/executors/context_executor/types.tolk b/contracts/contracts/ccip/executors/context_executor/types.tolk index 037eec84e..02668c816 100644 --- a/contracts/contracts/ccip/executors/context_executor/types.tolk +++ b/contracts/contracts/ccip/executors/context_executor/types.tolk @@ -1,12 +1,38 @@ // SPDX-License-Identifier: BUSL-1.1 +import "../../../lib/utils" + struct ContextExecutor_Data { - id: uint64; // 64bit addr space for a unique identity, for more use context C + id: uint64; // owner-scoped deployment nonce used to derive a unique executor address owner: address; context: Cell; forwardFrom: array
; } +struct ContextExecutor { + data: ContextExecutor_Data; +} + +fun ContextExecutor.getAddress( + code: cell, + id: uint64, + owner: address, + context: Cell, + forwardFrom: array
, +): AutoDeployAddress { + return AutoDeployAddress { + stateInit: ContractState { + code, + data: ContextExecutor_Data { + id, + owner, + context, + forwardFrom, + }.toCell(), + }, + }; +} + struct ContextExecutor_InMessageForward { senderAddress: address // an internal address from which the message arrived valueCoins: coins // ton amount attached to an incoming message diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index a259b482e..69c5925f4 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -6,8 +6,11 @@ import "@stdlib/lisp-lists" import "../../../lib/utils" import "../../../lib/access/ownable_2step" import "../../../lib/jetton/messages" +import "../../../lib/jetton/jetton_client" import "../../../lib/jetton/jetton-utils" import "../../common/types" +import "../../executors/context_executor/messages" +import "../../executors/context_executor/types" import "../../rmn_remote/lib" import "../lib/token_pool/entrypoint" import "../lib/token_pool/types" @@ -48,8 +51,8 @@ fun onInternalMessage(in: InMessage) { onClaimMinterAdmin(mutate st, in.senderAddress, msg.queryId); st.store(); } - ReturnExcessesBack => { - onReturnExcessesBack(mutate st, in.senderAddress, msg); + ContextExecutor_ForwardNotification => { + onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); } else => { @@ -58,9 +61,46 @@ fun onInternalMessage(in: InMessage) { } } -// TODO: implement me +fun deployBurnMintContextExecutor(executor: AutoDeployAddress): void { + val deploy = createMessage({ + bounce: BounceMode.NoBounce, + value: BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, + dest: executor, + body: beginCell().endCell().beginParse(), + }); + deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + fun onBouncedMessage(in: InMessageBounced) { - // TODO: use TokenPool.onBouncedMessage (lib) handler + val st = Storage.load(); + val rich = lazy RichBounceBody.fromSlice(in.bouncedBody); + val msg = lazy BurnMintTokenPool_BouncedMessage.fromCell(rich.originalBody); + match (msg) { + AskToBurn => { + assert(msg.customPayload != null) throw BurnMintTokenPool_Error.UnexpectedBurnBounce; + val context = lazy BurnMintTokenPool_BurnContext.fromCell(msg.customPayload!); + assert(in.senderAddress == context.wallet) throw BurnMintTokenPool_Error.UnexpectedBurnBounce; + + val operation = context.forwardPayload.load(); + var mutableSt = st; + refundLockOrBurnRateLimit(mutate mutableSt, operation); + mutableSt.store(); + sendLockOrBurnFailure(operation, BurnMintTokenPool_Error.UnexpectedBurnBounce as uint16); + } + MintNewJettons => { + assert(in.senderAddress == st.poolData.load().jettonClient().masterAddress) throw BurnMintTokenPool_Error.UnexpectedMintBounce; + val internalTransfer = lazy msg.internalTransferMsg.load(); + val contextCell = loadForwardPayloadAsCell(internalTransfer.forwardPayload); + assert(contextCell != null) throw BurnMintTokenPool_Error.UnexpectedMintBounce; + val context = lazy BurnMintTokenPool_MintContext.fromCell(contextCell!); + + val operation = context.forwardPayload.load(); + var mutableSt = st; + refundReleaseOrMintRateLimit(mutate mutableSt, operation); + mutableSt.store(); + sendReleaseOrMintFailure(operation, BurnMintTokenPool_Error.UnexpectedMintBounce as uint16); + } + } } /// @notice Token pool used for burn and mint tokens. This uses a burn and mint mechanism. @@ -71,25 +111,32 @@ fun onLockOrBurnTransferContinue( fwdPayload: TokenPool_LockOrBurnForwardPayload, ): Storage { val requestMsg = lazy fwdPayload.requestMsg.load(); - val prepared = lazy fwdPayload.prepared.load(); - - assert(!st.pendingBurns.get(requestMsg.queryId).isFound, BurnMintTokenPool_Error.PendingBurnAlreadyExists); - val jettonClient = st.poolData.load().jettonClient(); - st.pendingBurns.set(requestMsg.queryId, BurnMintTokenPool_PendingBurn { - forwardPayload: fwdPayload, - expectedSender: jettonClient.masterAddress, - }.toCell()); + val context = BurnMintTokenPool_BurnContext { + wallet: sender, + forwardPayload: fwdPayload.toCell(), + }; + val contextExecutorId = st.takeContextExecutorNextId(); + val executor = ContextExecutor.getAddress( + st.contextExecutorCode, + contextExecutorId, + contract.getAddress(), + context.toCell(), + [jettonClient.masterAddress], + ); + deployBurnMintContextExecutor(executor); + + val prepared = lazy fwdPayload.prepared.load(); val burnMsg = createMessage({ - bounce: BounceMode.RichBounce, // used to recover sendExcessesTo address (context) + bounce: BounceMode.RichBounce, value: BurnMintTokenPool_BURN_VALUE, dest: sender, body: AskToBurn { queryId: requestMsg.queryId, jettonAmount: prepared.destTokenAmount, - sendExcessesTo: contract.getAddress(), // TODO: use ContextExecutor addr as proxy - customPayload: null, + sendExcessesTo: executor.calculateAddress(), + customPayload: context.toCell(), }, }); burnMsg.send(SEND_MODE_PAY_FEES_SEPARATELY); @@ -105,10 +152,14 @@ fun onReleaseOrMintContinue( ): Storage? { assert(ctx != null, TokenPool_Error.UnsupportedOperation); var st = ctx!; - assert(!st.pendingMints.get(msg.queryId).isFound, BurnMintTokenPool_Error.PendingMintAlreadyExists); val jettonClient = st.poolData.load().jettonClient(); val request = lazy msg.request.load(); val details = lazy request.transfer.details.load(); + val forwardPayload = TokenPool_ReleaseOrMintForwardPayload { + originalSender: sender, + requestMsg: msg.toCell(), + prepared: prepared.toCell(), + }; val recipientWallet = calcAddressOfJettonWallet( details.receiver, @@ -116,17 +167,22 @@ fun onReleaseOrMintContinue( jettonClient.jettonWalletCode, ); - // TODO: use transfer.id + val context = BurnMintTokenPool_MintContext { + forwardPayload: forwardPayload.toCell(), + }; + val contextExecutorId = st.takeContextExecutorNextId(); + val executor = ContextExecutor.getAddress( + st.contextExecutorCode, + contextExecutorId, + contract.getAddress(), + context.toCell(), + [recipientWallet], + ); - st.pendingMints.set(msg.queryId, BurnMintTokenPool_PendingMint { - replyTo: msg.replyTo, - request: request.toCell(), - out: prepared.out.toCell(), - expectedSender: recipientWallet, - }.toCell()); + deployBurnMintContextExecutor(executor); val mint = createMessage({ - bounce: BounceMode.RichBounce, // used to recover sendExcessesTo address (context) + bounce: BounceMode.RichBounce, value: BurnMintTokenPool_MINT_VALUE, dest: jettonClient.masterAddress, body: MintNewJettons { @@ -137,9 +193,9 @@ fun onReleaseOrMintContinue( queryId: msg.queryId, jettonAmount: prepared.localAmount, transferInitiator: null, - sendExcessesTo: contract.getAddress(), + sendExcessesTo: executor.calculateAddress(), forwardTonAmount: 0, - forwardPayload: beginCell().storeUint(0, 1).endCell().beginParse(), + forwardPayload: beginCell().storeBool(true).storeRef(context.toCell()).endCell().beginParse(), }.toCell(), }, }); @@ -148,73 +204,163 @@ fun onReleaseOrMintContinue( return st; } -fun onClaimMinterAdmin( +fun refundLockOrBurnRateLimit( mutate st: Storage, - sender: address, - queryId: uint64, + forwardPayload: TokenPool_LockOrBurnForwardPayload, ) { - st.poolData.load().adminConfig.load().ownable.load().requireOwner(sender); - - val claim = createMessage({ - bounce: BounceMode.Only256BitsOfBody, - value: BurnMintTokenPool_CLAIM_ADMIN_VALUE, - dest: st.poolData.load().token(), - body: ClaimMinterAdmin { queryId }, - }); - claim.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + var pool = loadPool(st); + if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { + pool.releaseFastFinalityOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); + } else { + pool.releaseOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); + } + st.poolData = pool.data.toCell(); } -fun onReturnExcessesBack( +fun refundReleaseOrMintRateLimit( mutate st: Storage, - sender: address, - msg: ReturnExcessesBack, + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, ) { - val burnEntry = st.pendingBurns.get(msg.queryId); - if (burnEntry.isFound) { - val pending = burnEntry.loadValue().load(); - assert(sender == pending.expectedSender, BurnMintTokenPool_Error.UnexpectedBurnConfirmationSender); - - st.pendingBurns.delete(msg.queryId); - - var pool = loadPool(st); - return pool.onLockOrBurnTransferFinalize(pending.forwardPayload); + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + var pool = loadPool(st); + if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { + pool.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); + } else { + pool.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); } + st.poolData = pool.data.toCell(); +} - val mintEntry = st.pendingMints.get(msg.queryId); - assert(mintEntry.isFound, BurnMintTokenPool_Error.PendingMintNotFound); - - val pending = mintEntry.loadValue().load(); - assert(sender == pending.expectedSender, BurnMintTokenPool_Error.UnexpectedMintConfirmationSender); +fun sendLockOrBurnFailure( + forwardPayload: TokenPool_LockOrBurnForwardPayload, + errorCode: uint16, +) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val fail = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_LockOrBurnFailure { + queryId: requestMsg.queryId, + errorCode, + }, + }); + fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } +} - st.pendingMints.delete(msg.queryId); - val request = pending.request.load(); - val details = request.transfer.details.load(); - val out = pending.out.load(); +fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + val out = prepared.out; emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { remoteChainSelector: details.remoteChainSelector, details: TokenPool_ReleasedOrMintedDetails { token: details.localToken, - sender: contract.getAddress(), // TODO: who should we put as sender? (msg.sender on EVM) + sender: contract.getAddress(), amount: out.destinationAmount, recipient: details.receiver.toCell(), }.toCell(), }); - if (pending.replyTo != null) { + if (requestMsg.replyTo != null) { val notify = createMessage({ bounce: BounceMode.NoBounce, value: 0, - dest: pending.replyTo!, + dest: requestMsg.replyTo!, body: TokenPool_ReleaseOrMintFinished { - queryId: msg.queryId, - out: pending.out, + queryId: requestMsg.queryId, + out: prepared.out.toCell(), }, }); notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } } +fun sendReleaseOrMintFailure( + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, + errorCode: uint16, +) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val fail = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_ReleaseOrMintFailure { + queryId: requestMsg.queryId, + errorCode, + }, + }); + fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } +} + +fun onClaimMinterAdmin( + mutate st: Storage, + sender: address, + queryId: uint64, +) { + st.poolData.load().adminConfig.load().ownable.load().requireOwner(sender); + + val claim = createMessage({ + bounce: BounceMode.Only256BitsOfBody, + value: BurnMintTokenPool_CLAIM_ADMIN_VALUE, + dest: st.poolData.load().token(), + body: ClaimMinterAdmin { queryId }, + }); + claim.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + +fun onContextExecutorForwardNotification( + mutate st: Storage, + sender: address, + msg: ContextExecutor_ForwardNotification, +) { + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), TokenPool_Error.Unauthorized); + + val forwarded = msg.message.load(); + val returnExcesses = ReturnExcessesBack.fromCell(forwarded.body); + + val context = msg.context.load(); + match (context) { + BurnMintTokenPool_BurnContext => { + val operation = context.forwardPayload.load(); + assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; + + var pool = loadPool(st); + pool.onLockOrBurnTransferFinalize(operation); + st.poolData = pool.data.toCell(); + + return; + } + BurnMintTokenPool_MintContext => { + val operation = context.forwardPayload.load(); + assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; + + sendReleaseOrMintFinished(operation); + } + } +} + fun loadPool(st: Storage): TokenPool { return TokenPool { data: st.poolData.load(), @@ -262,14 +408,6 @@ get fun offRamp(remoteChainSelector: uint64): address? { return entry.isFound ? entry.loadValue() : null; } -get fun hasPendingBurn(queryId: uint64): bool { - return Storage.load().pendingBurns.get(queryId).isFound; -} - -get fun hasPendingMint(queryId: uint64): bool { - return Storage.load().pendingMints.get(queryId).isFound; -} - get fun getRMNProxy(): address { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } diff --git a/contracts/contracts/ccip/pools/burn_mint/errors.tolk b/contracts/contracts/ccip/pools/burn_mint/errors.tolk index 1d005d4a4..7eae24735 100644 --- a/contracts/contracts/ccip/pools/burn_mint/errors.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/errors.tolk @@ -4,10 +4,6 @@ const BurnMintTokenPool_FACILITY_NAME = "link.chain.ton.ccip.BurnMintTokenPool"; const BurnMintTokenPool_FACILITY_ID = 412; // (crc32() % 640) + 10 enum BurnMintTokenPool_Error { - PendingBurnAlreadyExists = BurnMintTokenPool_FACILITY_ID * 100 - PendingBurnNotFound - PendingMintAlreadyExists - PendingMintNotFound - UnexpectedBurnConfirmationSender - UnexpectedMintConfirmationSender + UnexpectedBurnBounce = BurnMintTokenPool_FACILITY_ID * 100 + UnexpectedMintBounce } diff --git a/contracts/contracts/ccip/pools/burn_mint/messages.tolk b/contracts/contracts/ccip/pools/burn_mint/messages.tolk index 2ae11bafb..59792be59 100644 --- a/contracts/contracts/ccip/pools/burn_mint/messages.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/messages.tolk @@ -1,5 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 import "../../../lib/jetton/messages" +import "../../executors/context_executor/messages" +import "types" struct (0x39898e4d) BurnMintTokenPool_ClaimMinterAdmin { queryId: uint64; @@ -7,9 +9,13 @@ struct (0x39898e4d) BurnMintTokenPool_ClaimMinterAdmin { type BurnMintTokenPool_InMessage = | BurnMintTokenPool_ClaimMinterAdmin - | ReturnExcessesBack; + | ContextExecutor_ForwardNotification; type BurnMintTokenPool_OutMessage = | ClaimMinterAdmin | MintNewJettons | AskToBurn; + +type BurnMintTokenPool_BouncedMessage = + | AskToBurn + | MintNewJettons; diff --git a/contracts/contracts/ccip/pools/burn_mint/storage.tolk b/contracts/contracts/ccip/pools/burn_mint/storage.tolk index 63123bc35..9c8461790 100644 --- a/contracts/contracts/ccip/pools/burn_mint/storage.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/storage.tolk @@ -8,11 +8,17 @@ import "types" struct Storage { poolData: Cell; - // Local storage used for MVP, won't scale to high volume of concurrent messages - // with 65,536 cells per contract limit. - // TODO: replace with sharded ContextExecutor per message - pendingBurns: map> = []; - pendingMints: map> = []; + contextExecutorCode: cell; + contextExecutorNextId: uint64; +} + +@inline +fun Storage.takeContextExecutorNextId(mutate self): uint64 { + val id = self.contextExecutorNextId; + assert(id != 0) throw 0xFFFF; + assert(id != 18446744073709551615) throw 0xFFFF; + self.contextExecutorNextId = id + 1; + return id; } fun Storage.load(): Storage { @@ -53,7 +59,7 @@ fun Storage.init(config: BurnMintTokenPool_InitConfig): Storage { remoteChainConfigs: createEmptyMap(), tokenTransferFeeConfigs: createEmptyMap(), }.toCell(), - pendingBurns: createEmptyMap(), - pendingMints: createEmptyMap(), + contextExecutorCode: config.contextExecutorCode, + contextExecutorNextId: 1, }; } \ No newline at end of file diff --git a/contracts/contracts/ccip/pools/burn_mint/types.tolk b/contracts/contracts/ccip/pools/burn_mint/types.tolk index 093107d90..5fc5d416d 100644 --- a/contracts/contracts/ccip/pools/burn_mint/types.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/types.tolk @@ -8,22 +8,25 @@ const BurnMintTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); const BurnMintTokenPool_CLAIM_ADMIN_VALUE = ton("0.05"); const BurnMintTokenPool_BURN_VALUE = ton("0.05"); const BurnMintTokenPool_MINT_VALUE = ton("0.1"); +const BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); -struct BurnMintTokenPool_PendingBurn { - forwardPayload: TokenPool_LockOrBurnForwardPayload; - expectedSender: address; +struct (0x0d49d523) BurnMintTokenPool_BurnContext { + wallet: address; + forwardPayload: Cell; } -struct BurnMintTokenPool_PendingMint { - replyTo: address?; - request: Cell; - out: Cell; - expectedSender: address; +struct (0x723f6ea1) BurnMintTokenPool_MintContext { + forwardPayload: Cell; } +type BurnMintTokenPool_ContextExecutorContext = + | BurnMintTokenPool_BurnContext + | BurnMintTokenPool_MintContext; + struct BurnMintTokenPool_InitConfig { owner: address; jettonClient: JettonClient; + contextExecutorCode: cell; tokenDecimals: uint8; router: address; rmnProxy: address; diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index d1161bbe2..0c24c3a39 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -9,6 +9,8 @@ import "../../../lib/jetton/messages" import "../../../lib/jetton/jetton-utils" import "../../../lib/jetton/jetton_client" import "../../common/types" +import "../../executors/context_executor/messages" +import "../../executors/context_executor/types" import "../../rmn_remote/lib" import "../lib/token_pool/entrypoint" import "../lib/token_pool/types" @@ -45,8 +47,8 @@ fun onInternalMessage(in: InMessage) { val msg = lazy LockReleaseTokenPool_InMessage.fromSlice(in.body); match (msg) { - ReturnExcessesBack => { - onReturnExcessesBack(mutate st, in.senderAddress, msg); + ContextExecutor_ForwardNotification => { + onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); } else => { @@ -55,13 +57,22 @@ fun onInternalMessage(in: InMessage) { } } -// TODO: implement me +fun deployLockReleaseContextExecutor(executor: AutoDeployAddress): void { + val deploy = createMessage({ + bounce: BounceMode.NoBounce, + value: LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, + dest: executor, + body: beginCell().endCell().beginParse(), + }); + deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + fun onBouncedMessage(in: InMessageBounced) { - // TODO: use TokenPool.onBouncedMessage (lib) handler - val msg = lazy LockReleaseTokenPool_BouncedMessage.fromSlice(in.bouncedBody.skipBouncedPrefix()); + var st = Storage.load(); + val rich = lazy RichBounceBody.fromSlice(in.bouncedBody); + val msg = lazy LockReleaseTokenPool_BouncedMessage.fromCell(rich.originalBody); match (msg) { AskToTransfer => { - var st = Storage.load(); onReleaseTransferBounced(mutate st, in.senderAddress, msg); st.store(); } @@ -76,11 +87,15 @@ fun onReleaseOrMintContinue( ): Storage? { assert(ctx != null, TokenPool_Error.UnsupportedOperation); var st = ctx!; - assert(!st.pendingReleases.get(msg.queryId).isFound, Error.PendingReleaseAlreadyExists); val jettonClient = st.poolData.load().jettonClient(); val request = lazy msg.request.load(); val details = lazy request.transfer.details.load(); + val forwardPayload = TokenPool_ReleaseOrMintForwardPayload { + originalSender: sender, + requestMsg: msg.toCell(), + prepared: prepared.toCell(), + }; val recipientWallet = calcAddressOfJettonWallet( details.receiver, @@ -88,33 +103,33 @@ fun onReleaseOrMintContinue( jettonClient.jettonWalletCode, ); - st.pendingReleases.set(msg.queryId, LockReleaseTokenPool_PendingRelease { - replyTo: msg.replyTo, - request: request.toCell(), - out: prepared.out.toCell(), - expectedSender: recipientWallet, - }.toCell()); - - val ownWallet = calcAddressOfJettonWallet( + val context = LockReleaseTokenPool_ReleaseContext { + forwardPayload: forwardPayload.toCell(), + }; + val contextExecutorId = st.takeContextExecutorNextId(); + val executor = ContextExecutor.getAddress( + st.contextExecutorCode, + contextExecutorId, contract.getAddress(), - jettonClient.masterAddress, - jettonClient.jettonWalletCode, + context.toCell(), + [recipientWallet], ); + deployLockReleaseContextExecutor(executor); + + val ownWallet = jettonClient.walletAddress(); + val transfer = createMessage({ - // TODO: use RichBounce to recover context, currently breaks with exitCode: 63 b/x - // `skipBouncedPrefix` in the handler. - bounce: true, + bounce: BounceMode.RichBounce, value: LockReleaseTokenPool_RELEASE_TRANSFER_VALUE, dest: ownWallet, body: AskToTransfer { queryId: msg.queryId, jettonAmount: prepared.localAmount, transferRecipient: details.receiver, - sendExcessesTo: contract.getAddress(), // TODO: ContextExecutor - customPayload: null, - forwardTonAmount: 0, // TODO: add fwdp amount - // TODO: add fwdp + sendExcessesTo: executor.calculateAddress(), + customPayload: context.toCell(), + forwardTonAmount: 0, forwardPayload: beginCell().storeBool(false).endCell().beginParse(), }, }); @@ -123,40 +138,48 @@ fun onReleaseOrMintContinue( return st; } -fun onReturnExcessesBack( +fun refundReleaseOrMintRateLimit( mutate st: Storage, - sender: address, - msg: ReturnExcessesBack, + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, ) { - val pendingEntry = st.pendingReleases.get(msg.queryId); - assert(pendingEntry.isFound, Error.PendingReleaseNotFound); - - val pending = pendingEntry.loadValue().load(); - assert(sender == pending.expectedSender, Error.UnexpectedReleaseConfirmationSender); + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + var pool = loadPool(st); + if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { + pool.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); + } else { + pool.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); + } + st.poolData = pool.data.toCell(); +} - st.pendingReleases.delete(msg.queryId); - val request = lazy pending.request.load(); +fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); val details = lazy request.transfer.details.load(); - val out = lazy pending.out.load(); + val prepared = lazy forwardPayload.prepared.load(); + val out = prepared.out; emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { remoteChainSelector: details.remoteChainSelector, details: TokenPool_ReleasedOrMintedDetails { token: details.localToken, - sender: contract.getAddress(), // TODO: who should we put as sender? (msg.sender on EVM) + sender: contract.getAddress(), amount: out.destinationAmount, recipient: details.receiver.toCell(), }.toCell(), }); - if (pending.replyTo != null) { + if (requestMsg.replyTo != null) { val notify = createMessage({ bounce: BounceMode.NoBounce, value: 0, - dest: pending.replyTo!, + dest: requestMsg.replyTo!, body: TokenPool_ReleaseOrMintFinished { - queryId: msg.queryId, - out: pending.out, + queryId: requestMsg.queryId, + out: prepared.out.toCell(), }, }); notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); @@ -168,41 +191,58 @@ fun onReleaseTransferBounced( sender: address, msg: AskToTransfer, ) { - val jettonClient = st.poolData.load().jettonClient(); - assert(jettonClient.isWallet(sender), Error.UnexpectedReleaseBounce); + assert(st.poolData.load().jettonClient().isWallet(sender)) throw Error.UnexpectedReleaseBounce; + assert(msg.customPayload != null) throw Error.UnexpectedReleaseBounce; - val pendingEntry = st.pendingReleases.get(msg.queryId); - assert(pendingEntry.isFound, Error.PendingReleaseNotFound); - - val pending = pendingEntry.loadValue().load(); - st.pendingReleases.delete(msg.queryId); - - // TODO: should use shared onBouncedMessage handler from TokenPool lib to - // reuse the logic across pool implementations. - // The release consumed inbound rate-limit capacity at admission, but the transfer - // bounced so no tokens were delivered. Return that capacity to the bucket. - val request = lazy pending.request.load(); - val details = lazy request.transfer.details.load(); - val out = lazy pending.out.load(); - var pool: TokenPool = loadPool(st); - pool.releaseInboundRateLimit(details.remoteChainSelector, out.destinationAmount); - st.poolData = pool.data.toCell(); + val context = lazy LockReleaseTokenPool_ReleaseContext.fromCell(msg.customPayload!); + val operation = context.forwardPayload.load(); + refundReleaseOrMintRateLimit(mutate st, operation); + sendReleaseOrMintFailure(operation, Error.UnexpectedReleaseBounce as uint16); +} - // TODO: we should always reply back - if (pending.replyTo != null) { +fun sendReleaseOrMintFailure( + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, + errorCode: uint16, +) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { val fail = createMessage({ bounce: BounceMode.NoBounce, value: 0, - dest: pending.replyTo!, + dest: requestMsg.replyTo!, body: TokenPool_ReleaseOrMintFailure { - queryId: msg.queryId, - errorCode: Error.UnexpectedReleaseBounce as uint16, + queryId: requestMsg.queryId, + errorCode, }, }); fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } } +fun onContextExecutorForwardNotification( + mutate st: Storage, + sender: address, + msg: ContextExecutor_ForwardNotification, +) { + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), TokenPool_Error.Unauthorized); + + val forwarded = msg.message.load(); + val returnExcesses = ReturnExcessesBack.fromCell(forwarded.body); + + val context = msg.context.load(); + val operation = context.forwardPayload.load(); + assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; + + sendReleaseOrMintFinished(operation); +} + fun loadPool(st: Storage): TokenPool { return TokenPool { data: st.poolData.load(), @@ -250,10 +290,6 @@ get fun offRamp(remoteChainSelector: uint64): address? { return entry.isFound ? entry.loadValue() : null; } -get fun hasPendingRelease(queryId: uint64): bool { - return Storage.load().pendingReleases.get(queryId).isFound; -} - get fun getRMNProxy(): address { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } diff --git a/contracts/contracts/ccip/pools/lock_release/errors.tolk b/contracts/contracts/ccip/pools/lock_release/errors.tolk index 4b91534fa..6a4e92d06 100644 --- a/contracts/contracts/ccip/pools/lock_release/errors.tolk +++ b/contracts/contracts/ccip/pools/lock_release/errors.tolk @@ -4,8 +4,5 @@ const LockReleaseTokenPool_FACILITY_NAME = "link.chain.ton.ccip.LockReleaseToken const LockReleaseTokenPool_FACILITY_ID = 263; // (crc32() % 640) + 10 enum Error { - PendingReleaseAlreadyExists = LockReleaseTokenPool_FACILITY_ID * 100 - PendingReleaseNotFound - UnexpectedReleaseConfirmationSender - UnexpectedReleaseBounce + UnexpectedReleaseBounce = LockReleaseTokenPool_FACILITY_ID * 100 } diff --git a/contracts/contracts/ccip/pools/lock_release/messages.tolk b/contracts/contracts/ccip/pools/lock_release/messages.tolk index 593cabc69..b2fd37310 100644 --- a/contracts/contracts/ccip/pools/lock_release/messages.tolk +++ b/contracts/contracts/ccip/pools/lock_release/messages.tolk @@ -1,8 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 import "../../../lib/jetton/messages" +import "../../executors/context_executor/messages" +import "types" type LockReleaseTokenPool_InMessage = - | ReturnExcessesBack; + | ContextExecutor_ForwardNotification; type LockReleaseTokenPool_OutMessage = | AskToTransfer; diff --git a/contracts/contracts/ccip/pools/lock_release/storage.tolk b/contracts/contracts/ccip/pools/lock_release/storage.tolk index 41ea7ac43..0d5386834 100644 --- a/contracts/contracts/ccip/pools/lock_release/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release/storage.tolk @@ -8,8 +8,17 @@ import "types" struct Storage { poolData: Cell; - // TODO: replace with sharded ContextExecutor per message - pendingReleases: map> = []; + contextExecutorCode: cell; + contextExecutorNextId: uint64; +} + +@inline +fun Storage.takeContextExecutorNextId(mutate self): uint64 { + val id = self.contextExecutorNextId; + assert(id != 0) throw 0xFFFF; + assert(id != 18446744073709551615) throw 0xFFFF; + self.contextExecutorNextId = id + 1; + return id; } fun Storage.load(): Storage { @@ -50,6 +59,7 @@ fun Storage.init(config: LockReleaseTokenPool_InitConfig): Storage { remoteChainConfigs: createEmptyMap(), tokenTransferFeeConfigs: createEmptyMap(), }.toCell(), - pendingReleases: createEmptyMap(), + contextExecutorCode: config.contextExecutorCode, + contextExecutorNextId: 1, }; } diff --git a/contracts/contracts/ccip/pools/lock_release/types.tolk b/contracts/contracts/ccip/pools/lock_release/types.tolk index dc8f8a483..660b03eba 100644 --- a/contracts/contracts/ccip/pools/lock_release/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release/types.tolk @@ -1,22 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 import "../../../lib/jetton/jetton_client" +import "../lib/token_pool/messages" import "../lib/token_pool/types" const LockReleaseTokenPool_CONTRACT_NAME = "link.chain.ton.ccip.LockReleaseTokenPool".literalSlice(); const LockReleaseTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); const LockReleaseTokenPool_RELEASE_TRANSFER_VALUE = ton("0.05"); const LockReleaseTokenPool_REPLY_VALUE = ton("0.01"); +const LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); -struct LockReleaseTokenPool_PendingRelease { - replyTo: address?; - request: Cell; - out: Cell; - expectedSender: address; +struct (0x54e6870c) LockReleaseTokenPool_ReleaseContext { + forwardPayload: Cell; } struct LockReleaseTokenPool_InitConfig { owner: address; jettonClient: JettonClient; + contextExecutorCode: cell; tokenDecimals: uint8; router: address; rmnProxy: address; diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index c0bfc460e..f0b93dff3 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -5,6 +5,7 @@ import "@stdlib/lisp-lists" import "../../../lib/utils" import "../../../lib/jetton/messages" +import "../../../lib/jetton/jetton_client" import "../../../lib/jetton/jetton-utils" import "../../common/types" import "../../rmn_remote/lib" @@ -105,11 +106,7 @@ fun onLockOrBurnTransferContinue( // Calculate addresses. val jettonClient = st.poolData.load().jettonClient(); - val ownWallet = calcAddressOfJettonWallet( - contract.getAddress(), - jettonClient.masterAddress, - jettonClient.jettonWalletCode, - ); + val ownWallet = jettonClient.walletAddress(); val lockboxWallet = calcAddressOfJettonWallet( st.lockbox, jettonClient.masterAddress, diff --git a/contracts/tests/ccip/ContextExecutor.spec.ts b/contracts/tests/ccip/ContextExecutor.spec.ts new file mode 100644 index 000000000..287f3af64 --- /dev/null +++ b/contracts/tests/ccip/ContextExecutor.spec.ts @@ -0,0 +1,81 @@ +import '@ton/test-utils' +import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox' +import { Cell, toNano } from '@ton/core' + +import { ContextExecutor } from '../../wrappers/gen/ccip/ContextExecutor' + +describe('ContextExecutor', () => { + let blockchain: Blockchain + let owner: SandboxContract + let forwarder: SandboxContract + let executor: SandboxContract + + beforeEach(async () => { + blockchain = await Blockchain.create() + owner = await blockchain.treasury('owner') + forwarder = await blockchain.treasury('forwarder') + + executor = blockchain.openContract( + ContextExecutor.fromStorage({ + id: 77n, + owner: owner.address, + context: Cell.EMPTY, + forwardFrom: [forwarder.address], + }), + ) + + await executor.sendDeploy(owner.getSender(), toNano('0.05')) + }) + + it('derives distinct addresses for distinct ids with identical context', () => { + const first = ContextExecutor.fromStorage({ + id: 1n, + owner: owner.address, + context: Cell.EMPTY, + forwardFrom: [forwarder.address], + }) + const second = ContextExecutor.fromStorage({ + id: 2n, + owner: owner.address, + context: Cell.EMPTY, + forwardFrom: [forwarder.address], + }) + + expect(first.address.equals(second.address)).toBe(false) + }) + + it('forwards at most one notification from an authorized sender', async () => { + const first = await forwarder.send({ + to: executor.address, + value: toNano('0.02'), + bounce: false, + body: Cell.EMPTY, + }) + + expect(first.transactions).toHaveTransaction({ + from: forwarder.address, + to: executor.address, + success: true, + }) + expect(first.transactions).toHaveTransaction({ + from: executor.address, + to: owner.address, + success: true, + }) + + const second = await forwarder.send({ + to: executor.address, + value: toNano('0.02'), + bounce: false, + body: Cell.EMPTY, + }) + + const repeatedNotifications = second.transactions.filter( + (tx: any) => + tx.inMessage?.info?.src?.equals?.(executor.address) && + tx.inMessage?.info?.dest?.equals?.(owner.address), + ) + + expect(repeatedNotifications).toHaveLength(0) + }) +}) \ No newline at end of file diff --git a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts index 1a2d1e516..c7fbd475e 100644 --- a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts @@ -1,6 +1,6 @@ import '@ton/test-utils' import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox' -import { Address, Cell, Dictionary, toNano } from '@ton/core' +import { Address, beginCell, Cell, toNano } from '@ton/core' import { JettonMinter, JettonWallet } from '../../../wrappers/examples/jetton' import { CCTJettonMinter } from '../../../wrappers/ccip/CCTJettonMinter' import { CCTJettonMinterCode, CCTJettonWalletCode } from '../../../wrappers/ccip/CCTJettonCode' @@ -30,10 +30,41 @@ import { } from '../../../wrappers/gen/ccip/pools/TokenPool' import { TokenPool_LockOrBurnWithdraw } from '../../../wrappers/gen/ccip/pools/BurnMintTokenPool' import { BurnMintTokenPool, JettonClient } from '../../../wrappers/gen/ccip/pools/BurnMintTokenPool' -import { runTokenPoolBehaviorTests, runTokenPoolAsyncHookBehaviorTests } from './TokenPool.behavior' +import { + ContextExecutor, + ContextExecutor_ForwardNotification, + ContextExecutor_InMessageForward, +} from '../../../wrappers/gen/ccip/ContextExecutor' +import { + runTokenPoolAsyncHookBehaviorTests, + runTokenPoolBehaviorTests, +} from './TokenPool.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' +function buildSpoofedExecutorForwardNotification(senderAddress: Address): Cell { + const forwarded = ContextExecutor_InMessageForward.toCell( + ContextExecutor_InMessageForward.create({ + senderAddress, + valueCoins: 0n, + valueExtra: new Map(), + originalForwardFee: 0n, + createdLt: 0n, + createdAt: 0n, + body: Cell.EMPTY, + }), + ) + + return beginCell() + .storeUint(ContextExecutor_ForwardNotification.PREFIX, 32) + .storeUint(999n, 64) + .storeRef(Cell.EMPTY) + .storeUint(0, 8) + .storeMaybeRef(null) + .storeRef(forwarded) + .endCell() +} + describe('BurnMintTokenPool', () => { let blockchain: Blockchain let deployer: SandboxContract @@ -105,18 +136,16 @@ describe('BurnMintTokenPool', () => { advancedPoolHooks: null, }), mirroredPolicy: TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), - cursedSubjects: CursedSubjects.create({ - data: new Set(), - }), + onRamps: new Map(), + offRamps: new Map(), + cursedSubjects: CursedSubjects.create({ data: new Set() }), }), tokenDecimals: 9n, remoteChainConfigs: new Map(), tokenTransferFeeConfigs: new Map(), }), - pendingMints: new Map(), - pendingBurns: new Map(), + contextExecutorCode: ContextExecutor.CodeCell, + contextExecutorNextId: 1n, }), ) await burnMintPool.sendDeploy(deployer.getSender(), toNano('2')) @@ -299,11 +328,6 @@ describe('BurnMintTokenPool', () => { } }) - it('has no pending burn or mint by default', async () => { - expect(await burnMintPool.getHasPendingBurn(300n)).toBe(false) - expect(await burnMintPool.getHasPendingMint(301n)).toBe(false) - }) - it('rejects claim-minter-admin from non-owner sender', async () => { const result = await burnMintPool.sendBurnMintTokenPoolClaimMinterAdmin( unauthorized.getSender(), @@ -402,7 +426,7 @@ describe('BurnMintTokenPool', () => { }) }) - it('burns tokens on lockOrBurn path and clears pending burn on confirmation', async () => { + it('burns tokens on lockOrBurn path and finalizes through the executor notification', async () => { const deployerWallet = await userWallet(deployer.address) const poolWallet = await userWallet(burnMintPool.address) @@ -479,7 +503,7 @@ describe('BurnMintTokenPool', () => { feeAmount: 0n, destTokenAmount: jettonAmount, out: TokenPool_LockOrBurnOutV1.create({ - destTokenAddress: destTokenAddress, + destTokenAddress, destPoolData: Cell.EMPTY, }), }), @@ -501,10 +525,8 @@ describe('BurnMintTokenPool', () => { withdrawBody.forwardPayload, ) - // Jetton wallet sends TransferNotificationForRecipient to pool - // Pool receives notification → validates forward payload → creates pending burn → sends AskToBurn to minter - // Minter receives AskToBurn → burns → sends ReturnExcessesBack to pool - // Pool receives ReturnExcessesBack → clears pending burn → finalizes → TokenPool_LockOrBurnFinished + // Jetton wallet sends TransferNotificationForRecipient to the pool. + // Pool deploys a context executor, asks its own wallet to burn, then finalizes on executor forward notification. const result2 = await deployerWallet.sendTransfer(deployer.getSender(), { value: toNano('0.5'), message: { @@ -518,8 +540,6 @@ describe('BurnMintTokenPool', () => { }, }) - // Verify full async flow completed - expect(await burnMintPool.getHasPendingBurn(queryId)).toBe(false) expect(await poolWallet.getJettonBalance()).toEqual(0n) expect(result2.transactions).toHaveTransaction({ @@ -530,7 +550,7 @@ describe('BurnMintTokenPool', () => { }) }) - it('mints tokens on releaseOrMint path and clears pending mint on confirmation', async () => { + it('mints tokens on releaseOrMint path and finalizes through the executor notification', async () => { const result = await burnMintPool.sendTokenPoolReleaseOrMint( offRamp.getSender(), toNano('0.6'), @@ -547,7 +567,7 @@ describe('BurnMintTokenPool', () => { localToken: cctMinter.address, }), }), - sourcePoolAddress: sourcePoolAddress, + sourcePoolAddress, sourcePoolData: null, offchainTokenData: null, }), @@ -568,8 +588,6 @@ describe('BurnMintTokenPool', () => { success: true, }) - expect(await burnMintPool.getHasPendingMint(22n)).toBe(false) - expect(result.transactions).toHaveTransaction({ from: burnMintPool.address, to: deployer.address, @@ -600,7 +618,7 @@ describe('BurnMintTokenPool', () => { localToken: cctMinter.address, }), }), - sourcePoolAddress: sourcePoolAddress, + sourcePoolAddress, sourcePoolData: null, offchainTokenData: null, }), @@ -621,13 +639,36 @@ describe('BurnMintTokenPool', () => { }) const releaseResponses = result.transactions.filter((tx: any) => { + const body = tx.inMessage?.body + if (!body) { + return false + } + + const slice = body.beginParse() + if (slice.remainingBits < 32) { + return false + } + return ( tx.inMessage?.info?.src?.equals?.(burnMintPool.address) && - tx.inMessage?.body?.beginParse?.().preloadUint?.(32) === - TokenPool_ReleaseOrMintFinished.PREFIX + slice.preloadUint(32) === TokenPool_ReleaseOrMintFinished.PREFIX ) }) expect(releaseResponses.length).toBe(0) - expect(await burnMintPool.getHasPendingMint(305n)).toBe(false) + }) + + it('rejects forged executor forward notifications', async () => { + const forged = await unauthorized.send({ + to: burnMintPool.address, + value: toNano('0.1'), + bounce: false, + body: buildSpoofedExecutorForwardNotification(unauthorized.address), + }) + + expect(forged.transactions).toHaveTransaction({ + from: unauthorized.address, + to: burnMintPool.address, + success: false, + }) }) }) diff --git a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts index bc3f0f089..6a6721f39 100644 --- a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts @@ -271,6 +271,21 @@ describe('LockReleaseLockboxTokenPool', () => { } }) + const setupTokenPoolBehaviorContext = async () => { + await jettonMinter.sendMint(deployer.getSender(), { + value: toNano('2'), + message: { + queryId: 0n, + destination: jettonLockBox.address, + tonAmount: toNano('0.5'), + jettonAmount: toNano('10'), + from: deployer.address, + responseDestination: deployer.address, + forwardTonAmount: toNano('0.3'), + }, + }) + } + runTokenPoolBehaviorTests('LockReleaseLockboxTokenPool', async () => ({ pool, deployer, @@ -282,7 +297,9 @@ describe('LockReleaseLockboxTokenPool', () => { destTokenAddress, sourcePoolAddress, localToken: jettonMinter.address, - })) + }), { + setup: setupTokenPoolBehaviorContext, + }) // Async hook behavior tests (TON-TP/6) runTokenPoolAsyncHookBehaviorTests('LockReleaseLockboxTokenPool', async () => { diff --git a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts index 8c2637c40..359243490 100644 --- a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts @@ -12,6 +12,7 @@ import { TokenPool_AdminConfig, TokenPool_DynamicConfig, TokenPool_MirroredPolicy, + TokenPool_ReleaseOrMintFailure, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, TokenPool_LockOrBurnInV1, @@ -29,11 +30,42 @@ import { JettonClient, LockReleaseTokenPool, } from '../../../wrappers/gen/ccip/pools/LockReleaseTokenPool' +import { + ContextExecutor, + ContextExecutor_ForwardNotification, + ContextExecutor_InMessageForward, +} from '../../../wrappers/gen/ccip/ContextExecutor' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' -import { runTokenPoolBehaviorTests, runTokenPoolAsyncHookBehaviorTests } from './TokenPool.behavior' +import { + runTokenPoolAsyncHookBehaviorTests, + runTokenPoolBehaviorTests, +} from './TokenPool.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' +function buildSpoofedExecutorForwardNotification(senderAddress: Address): Cell { + const forwarded = ContextExecutor_InMessageForward.toCell( + ContextExecutor_InMessageForward.create({ + senderAddress, + valueCoins: 0n, + valueExtra: new Map(), + originalForwardFee: 0n, + createdLt: 0n, + createdAt: 0n, + body: Cell.EMPTY, + }), + ) + + return beginCell() + .storeUint(ContextExecutor_ForwardNotification.PREFIX, 32) + .storeUint(999n, 64) + .storeRef(Cell.EMPTY) + .storeUint(0, 8) + .storeMaybeRef(null) + .storeRef(forwarded) + .endCell() +} + describe('LockReleaseTokenPool', () => { let blockchain: Blockchain let deployer: SandboxContract @@ -116,17 +148,16 @@ describe('LockReleaseTokenPool', () => { advancedPoolHooks: null, }), mirroredPolicy: TokenPool_MirroredPolicy.create({ - onRamps: new Map(), - offRamps: new Map(), - cursedSubjects: CursedSubjects.create({ - data: new Set(), - }), + onRamps: new Map(), + offRamps: new Map(), + cursedSubjects: CursedSubjects.create({ data: new Set() }), }), tokenDecimals: 9n, remoteChainConfigs: new Map(), tokenTransferFeeConfigs: new Map(), }), - pendingReleases: new Map(), + contextExecutorCode: ContextExecutor.CodeCell, + contextExecutorNextId: 1n, }), ) await lockReleasePool.sendDeploy(deployer.getSender(), toNano('2')) @@ -139,26 +170,26 @@ describe('LockReleaseTokenPool', () => { toNano('0.2'), { queryId: 1n, - remoteChainSelectorsToRemove: [], - chainsToAdd: [ + remoteChainSelectorsToRemove: [], + chainsToAdd: [ TokenPool_ChainUpdate.create({ remoteChainSelector, - remotePoolAddresses: [sourcePoolAddress], - remoteTokenAddress: destTokenAddress, - rateLimitConfigs: TokenPool_RateLimitConfigPair.create({ - outbound: RateLimiter_Config.create({ + remotePoolAddresses: [sourcePoolAddress], + remoteTokenAddress: destTokenAddress, + rateLimitConfigs: TokenPool_RateLimitConfigPair.create({ + outbound: RateLimiter_Config.create({ isEnabled: true, capacity: toNano('100'), rate: 1n, - }), - inbound: RateLimiter_Config.create({ + }), + inbound: RateLimiter_Config.create({ isEnabled: true, capacity: toNano('100'), rate: 1n, + }), }), - }), }), - ], + ], }, ) @@ -214,6 +245,21 @@ describe('LockReleaseTokenPool', () => { } }) + const setupTokenPoolBehaviorContext = async () => { + await jettonMinter.sendMint(deployer.getSender(), { + value: toNano('1'), + message: { + queryId: 0n, + destination: lockReleasePool.address, + tonAmount: toNano('0.05'), + jettonAmount: toNano('10'), + from: deployer.address, + responseDestination: deployer.address, + forwardTonAmount: 0n, + }, + }) + } + runTokenPoolBehaviorTests('LockReleaseTokenPool', async () => ({ pool, deployer, @@ -225,7 +271,9 @@ describe('LockReleaseTokenPool', () => { destTokenAddress, sourcePoolAddress, localToken: jettonMinter.address, - })) + }), { + setup: setupTokenPoolBehaviorContext, + }) // Async hook behavior tests (TON-TP/6) runTokenPoolAsyncHookBehaviorTests('LockReleaseTokenPool', async () => { @@ -263,10 +311,6 @@ describe('LockReleaseTokenPool', () => { } }) - it('has no pending release by default', async () => { - expect(await lockReleasePool.getHasPendingRelease(999n)).toBe(false) - }) - it('reverts lockOrBurn when forwarded amount does not match transfer amount', async () => { const onRampWallet = await userWallet(jettonSender.address) const poolWallet = await userWallet(lockReleasePool.address) @@ -349,7 +393,7 @@ describe('LockReleaseTokenPool', () => { localToken: jettonMinter.address, }), }), - sourcePoolAddress: sourcePoolAddress, + sourcePoolAddress, sourcePoolData: null, offchainTokenData: null, }), @@ -363,7 +407,6 @@ describe('LockReleaseTokenPool', () => { to: lockReleasePool.address, success: false, }) - expect(await lockReleasePool.getHasPendingRelease(46n)).toBe(false) }) it('refunds inbound rate-limit capacity when a release bounces (TON-TP/5)', async () => { @@ -390,7 +433,7 @@ describe('LockReleaseTokenPool', () => { localToken: jettonMinter.address, }), }), - sourcePoolAddress: sourcePoolAddress, + sourcePoolAddress, sourcePoolData: null, offchainTokenData: null, }), @@ -405,7 +448,18 @@ describe('LockReleaseTokenPool', () => { inMessageBounced: true, success: true, }) - expect(await lockReleasePool.getHasPendingRelease(77n)).toBe(false) + + expect(result.transactions).toHaveTransaction({ + from: lockReleasePool.address, + to: deployer.address, + success: true, + op: TokenPool_ReleaseOrMintFailure.PREFIX, + body(body) { + if (!body) return false + const failure = TokenPool_ReleaseOrMintFailure.fromSlice(body.beginParse()) + return failure.queryId === 77n + }, + }) // Consumed capacity (5) was refunded: the bucket is restored to its starting balance. const after = await lockReleasePool.getCurrentRateLimiterState(remoteChainSelector, false) @@ -456,7 +510,7 @@ describe('LockReleaseTokenPool', () => { expect(await poolWallet.getJettonBalance()).toEqual(toNano('3')) }) - it('releases tokens from pool custody after off-ramp request and clears pending state on confirmation', async () => { + it('releases tokens from pool custody after off-ramp request and finalizes through the executor notification', async () => { const poolWallet = await userWallet(lockReleasePool.address) const recipientWallet = await userWallet(recipient.address) @@ -489,7 +543,7 @@ describe('LockReleaseTokenPool', () => { localToken: jettonMinter.address, }), }), - sourcePoolAddress: sourcePoolAddress, + sourcePoolAddress, sourcePoolData: null, offchainTokenData: null, }), @@ -506,7 +560,6 @@ describe('LockReleaseTokenPool', () => { expect(await recipientWallet.getJettonBalance()).toEqual(toNano('2')) expect(await poolWallet.getJettonBalance()).toEqual(toNano('3')) - expect(await lockReleasePool.getHasPendingRelease(22n)).toBe(false) expect(result.transactions).toHaveTransaction({ from: lockReleasePool.address, @@ -521,15 +574,92 @@ describe('LockReleaseTokenPool', () => { }) }) + it('releases tokens with null replyTo without emitting a response message', async () => { + const poolWallet = await userWallet(lockReleasePool.address) + const recipientWallet = await userWallet(recipient.address) + + await jettonMinter.sendMint(deployer.getSender(), { + value: toNano('1'), + message: { + queryId: 0n, + destination: lockReleasePool.address, + tonAmount: toNano('0.05'), + jettonAmount: toNano('4'), + from: deployer.address, + responseDestination: deployer.address, + forwardTonAmount: 0n, + }, + }) + + const result = await lockReleasePool.sendTokenPoolReleaseOrMint( + offRamp.getSender(), + toNano('0.4'), + { + queryId: 223n, + request: TokenPool_ReleaseOrMintInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 223n, + details: TokenPool_TransferDetails.create({ + originalSender: sourcePoolAddress, + remoteChainSelector, + receiver: recipient.address, + amount: toNano('1'), + localToken: jettonMinter.address, + }), + }), + sourcePoolAddress, + sourcePoolData: null, + offchainTokenData: null, + }), + requestedFinalityConfig: 0n, + replyTo: null, + }, + ) + + expect(await recipientWallet.getJettonBalance()).toEqual(toNano('1')) + expect(await poolWallet.getJettonBalance()).toEqual(toNano('3')) + + const releaseResponses = result.transactions.filter((tx: any) => { + const body = tx.inMessage?.body + if (!body) { + return false + } + + const slice = body.beginParse() + if (slice.remainingBits < 32) { + return false + } + + return ( + tx.inMessage?.info?.src?.equals?.(lockReleasePool.address) && + slice.preloadUint(32) === TokenPool_ReleaseOrMintFinished.PREFIX + ) + }) + expect(releaseResponses.length).toBe(0) + }) + + it('rejects forged executor forward notifications', async () => { + const forged = await recipient.send({ + to: lockReleasePool.address, + value: toNano('0.1'), + bounce: false, + body: buildSpoofedExecutorForwardNotification(recipient.address), + }) + + expect(forged.transactions).toHaveTransaction({ + from: recipient.address, + to: lockReleasePool.address, + success: false, + }) + }) + it('mirrors cursed state locally and blocks release while cursed', async () => { const curseUpdate = await lockReleasePool.sendTokenPoolSetCursedSubjects( deployer.getSender(), toNano('0.2'), { queryId: 901n, - cursedSubjects: CursedSubjects.create({ - data: new Set([remoteChainSelector]), - }), + cursedSubjects: CursedSubjects.create({ data: new Set([remoteChainSelector]) }), }, ) diff --git a/contracts/tests/ccip/pools/TokenPool.behavior.ts b/contracts/tests/ccip/pools/TokenPool.behavior.ts index 979ae8ee2..38eaa2ff9 100644 --- a/contracts/tests/ccip/pools/TokenPool.behavior.ts +++ b/contracts/tests/ccip/pools/TokenPool.behavior.ts @@ -15,6 +15,8 @@ import { TokenPool_RampUpdate, TokenPool_RateLimitConfigPair, TokenPool_ReleaseOrMint, + TokenPool_ReleaseOrMintFailure, + TokenPool_ReleaseOrMintFinished, TokenPool_ReleaseOrMintForwardPayload, TokenPool_ReleaseOrMintInV1, TokenPool_ReleaseOrMintOutV1, @@ -38,6 +40,10 @@ export type TokenPoolBehaviorContext = { localToken: Address } +export type TokenPoolBehaviorHooks = { + setup?: (ctx: TokenPoolBehaviorContext) => Promise +} + function releaseRequest( ctx: TokenPoolBehaviorContext, overrides: Partial = {}, @@ -63,7 +69,15 @@ function releaseRequest( export function runTokenPoolBehaviorTests( name: string, setup: () => Promise, + hooks: TokenPoolBehaviorHooks = {}, ) { + const baseSetup = setup + setup = async () => { + const ctx = await baseSetup() + await hooks.setup?.(ctx) + return ctx + } + describe(`${name} TokenPool behavior`, () => { it('mirrors ramp access and supported chain state after setup', async () => { const ctx = await setup() @@ -124,9 +138,7 @@ export function runTokenPoolBehaviorTests( await ctx.pool.sendTokenPoolSetCursedSubjects(ctx.deployer.getSender(), toNano('0.2'), { queryId: 901n, - cursedSubjects: CursedSubjects.create({ - data: new Set([ctx.remoteChainSelector]), - }), + cursedSubjects: CursedSubjects.create({ data: new Set([ctx.remoteChainSelector]) }), }) expect(await ctx.pool.getVerifyNotCursed(ctx.remoteChainSelector)).toBe(false) @@ -216,9 +228,7 @@ export function runTokenPoolBehaviorTests( toNano('0.2'), { queryId: 904n, - cursedSubjects: CursedSubjects.create({ - data: new Set([ctx.remoteChainSelector]), - }), + cursedSubjects: CursedSubjects.create({ data: new Set([ctx.remoteChainSelector]) }), }, ) @@ -233,17 +243,13 @@ export function runTokenPoolBehaviorTests( const ctx = await setup() await ctx.pool.sendTokenPoolSetCursedSubjects(ctx.deployer.getSender(), toNano('0.2'), { queryId: 901n, - cursedSubjects: CursedSubjects.create({ - data: new Set([ctx.remoteChainSelector]), - }), + cursedSubjects: CursedSubjects.create({ data: new Set([ctx.remoteChainSelector]) }), }) expect(await ctx.pool.getVerifyNotCursed(ctx.remoteChainSelector)).toBe(false) await ctx.pool.sendTokenPoolSetCursedSubjects(ctx.deployer.getSender(), toNano('0.2'), { queryId: 902n, - cursedSubjects: CursedSubjects.create({ - data: new Set(), - }), + cursedSubjects: CursedSubjects.create({ data: new Set() }), }) expect(await ctx.pool.getVerifyNotCursed(ctx.remoteChainSelector)).toBe(true) }) @@ -519,6 +525,88 @@ export function runTokenPoolBehaviorTests( }) expect(await ctx.pool.getIsSupportedChain(ctx.remoteChainSelector)).toBe(true) }) + + it('reuses the same caller queryId safely across repeated release continuations', async () => { + const ctx = await setup() + const amount = toNano('1') + const queryId = 700n + + const repeatedRequest = { + queryId, + request: releaseRequest(ctx, { + transfer: TokenPool_Transfer.create({ + id: queryId, + details: TokenPool_TransferDetails.create({ + originalSender: ctx.sourcePoolAddress, + remoteChainSelector: ctx.remoteChainSelector, + receiver: ctx.recipient.address, + amount, + localToken: ctx.localToken, + }), + }), + }), + requestedFinalityConfig: 0n, + replyTo: ctx.deployer.address, + } + + const first = await ctx.pool.sendTokenPoolReleaseOrMint( + ctx.offRamp.getSender(), + toNano('0.6'), + repeatedRequest, + ) + const second = await ctx.pool.sendTokenPoolReleaseOrMint( + ctx.offRamp.getSender(), + toNano('0.6'), + repeatedRequest, + ) + + const allTransactions = [...first.transactions, ...second.transactions] + const completions = allTransactions.filter((tx: any) => { + const body = tx.inMessage?.body + if (!body) { + return false + } + + const slice = body.beginParse() + if (slice.remainingBits < 32) { + return false + } + + if ( + !tx.inMessage?.info?.src?.equals?.(ctx.pool.address) || + slice.preloadUint(32) !== TokenPool_ReleaseOrMintFinished.PREFIX + ) { + return false + } + + const response = TokenPool_ReleaseOrMintFinished.fromSlice(slice) + return response.queryId === queryId && response.out.destinationAmount === amount + }) + + const failures = allTransactions.filter((tx: any) => { + const body = tx.inMessage?.body + if (!body) { + return false + } + + const slice = body.beginParse() + if (slice.remainingBits < 32) { + return false + } + + if ( + !tx.inMessage?.info?.src?.equals?.(ctx.pool.address) || + slice.preloadUint(32) !== TokenPool_ReleaseOrMintFailure.PREFIX + ) { + return false + } + + return TokenPool_ReleaseOrMintFailure.fromSlice(slice).queryId === queryId + }) + + expect(completions).toHaveLength(2) + expect(failures).toHaveLength(0) + }) }) } @@ -578,12 +666,12 @@ export function runTokenPoolAsyncHookBehaviorTests( originalSender: ctx.deployer.address, requestMsg: TokenPool_LockOrBurn.create({ queryId: 0n, - request: request, + request, requestedFinalityConfig: 0n, tokenArgs: null, replyTo: null, }), - prepared: prepared, + prepared, }) return TokenPool_LockOrBurnForwardPayload.toCell(fwdp) } @@ -606,11 +694,11 @@ export function runTokenPoolAsyncHookBehaviorTests( originalSender: ctx.offRamp.address, requestMsg: TokenPool_ReleaseOrMint.create({ queryId: 0n, - request: request, + request, requestedFinalityConfig: 0n, replyTo: null, }), - prepared: prepared, + prepared, }) return TokenPool_ReleaseOrMintForwardPayload.toCell(fwdp) } @@ -645,7 +733,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const result = await ctx.pool.sendTokenPoolLockOrBurn(ctx.deployer.getSender(), toNano('1'), { queryId: 2n, - request: request, + request, requestedFinalityConfig: 0n, tokenArgs: null, replyTo: ctx.deployer.address, @@ -675,7 +763,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const result = await ctx.pool.sendTokenPoolLockOrBurn(ctx.deployer.getSender(), toNano('1'), { queryId: 1n, - request: request, + request, requestedFinalityConfig: 0n, tokenArgs: null, replyTo: ctx.deployer.address, @@ -715,7 +803,7 @@ export function runTokenPoolAsyncHookBehaviorTests( toNano('1'), { queryId: 2n, - request: request, + request, requestedFinalityConfig: 0n, replyTo: ctx.offRamp.address, }, @@ -748,7 +836,7 @@ export function runTokenPoolAsyncHookBehaviorTests( toNano('1'), { queryId: 1n, - request: request, + request, requestedFinalityConfig: 0n, replyTo: ctx.offRamp.address, }, @@ -773,7 +861,48 @@ export function runTokenPoolAsyncHookBehaviorTests( from: ctx.pool.address, to: ctx.offRamp.address, success: true, + op: TokenPool_ReleaseOrMintFailure.PREFIX, + body(body) { + if (!body) return false + const failure = TokenPool_ReleaseOrMintFailure.fromSlice(body.beginParse()) + return failure.queryId === 1n + }, + }) + }) + + it('does not emit ReleaseOrMintFailure when async postflight fails and replyTo is null', async () => { + const ctx = await setup() + + const request = releaseRequest(ctx) + + const result = await ctx.pool.sendTokenPoolReleaseOrMint( + ctx.offRamp.getSender(), + toNano('1'), + { + queryId: 3n, + request, + requestedFinalityConfig: 0n, + replyTo: null, + }, + ) + + expect(result.transactions).toHaveTransaction({ + from: ctx.hooks.address, + to: ctx.pool.address, + success: true, + }) + + const failures = result.transactions.filter((tx: any) => { + const body = tx.inMessage?.body + if (!body) { + return false + } + + const slice = body.beginParse() + return slice.remainingBits >= 32 && slice.preloadUint(32) === TokenPool_ReleaseOrMintFailure.PREFIX }) + + expect(failures).toHaveLength(0) }) // === Inline mode (replyTo = null for LockOrBurn) === @@ -785,7 +914,7 @@ export function runTokenPoolAsyncHookBehaviorTests( const result = await ctx.pool.sendTokenPoolLockOrBurn(ctx.deployer.getSender(), toNano('1'), { queryId: 100n, - request: request, + request, requestedFinalityConfig: 0n, tokenArgs: null, replyTo: null, @@ -808,7 +937,7 @@ export function runTokenPoolAsyncHookBehaviorTests( toNano('1'), { queryId: 100n, - request: request, + request, requestedFinalityConfig: 0n, replyTo: ctx.deployer.address, }, diff --git a/contracts/wrappers/gen/ccip/ContextExecutor.ts b/contracts/wrappers/gen/ccip/ContextExecutor.ts index 1467cc806..b35b5706b 100644 --- a/contracts/wrappers/gen/ccip/ContextExecutor.ts +++ b/contracts/wrappers/gen/ccip/ContextExecutor.ts @@ -506,7 +506,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class ContextExecutor implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECFAEAA5AAART/APSkE/S88sgLAQIBYgIDAgLOBAUCASAMDQIBIAYHAElO2i7ftsIjJwIW+Ikly5jhAhpFIzb4EkxwWVXwR/2zHg6F8EcIAas+JHyQO1E0NM/+kjUbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidH4kviX+Jj4k3D4OviU+JUqyM7J8AHjAl8EhA8BxwDy9IAgC9wg0NcsIicw92TjAtcsJlamjoSOZmxh0z/U1woAyM+ST5bvFhPLPyfPCz8lzxQkb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU5BvgVj6UgGk5AHJAqHkMDECywf0AMwhzwoAycjPhQgT+lJxzwtuEszJAYMGgEDjBPsAf+CAJCgB2NALIyz/6Uswhb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU2BvgVj6UgGk5AHJAqHkMDEzzwsH9ADJ7VQB/jhfBjKCAKpQXccF8vTTP9RvAAHTB/QFkyBus44Q0PQEmvpIUERvjCPHABTmMOgwIW+IuvKJcMjLP8nIz5JPlu8WFMs/Js8LPyLPFCFviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTYG+BWPpSAaTkAckCoeQwMQLLB/QAE8zPgckLAOwwVHqYU6nwAo5mBsj6UlAF+gIT9AAB+gLLP8sfzMnIz5FW0ErmJc8LPyPPFCJviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTcG+BWPpSAaTkAckCoeQwMQLLB/QAzMnIz4UIUkD6UnHPC27MyYBA+wB/4Gxh0McAACbIz4UIFPpScc8LbhPMyYBA+wB/AgEgDg8Abbzsh2omhpn/0kGOoYt4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTowCAnIQEQIBIBITAF6pX40I2xpbmsuY2hhaW4udG9uLmNjaXAuQ29udGV4dEV4ZWN1dG9ygi1MC4xLjCABsqFrtRNDTPzH6SDHUMW8AAdMH9ASTIW6zjhIB0PQEmvpIUFVvjCTHABXmMAHoMSJviFi68onRAG20o72omhpn5j9JGoYt4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTowAG20AX2omhpn5j9JBjqN4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTow'); + static CodeCell = c.Cell.fromBase64('te6ccgECFAEAA5UAART/APSkE/S88sgLAQIBYgIDAgLOBAUCASAMDQIBIAYHAElO2i7ftsIjJwIW+Ikly5jhAhpFIzb4EkxwWVXwR/2zHg6F8EcIAas+JHyQO1E0NM/+kjUbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidH4kviX+Jj4k3D4OviU+JUqyM7J8AHjAl8EhA8BxwDy9IAgC9wg0NcsIicw92TjAtcsJlamjoSOZmxh0z/U1woAyM+ST5bvFhPLPyfPCz8lzxQkb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU5BvgVj6UgGk5AHJAqHkMDECywf0AMwhzwoAycjPhQgT+lJxzwtuEszJAYMGgEDjBPsAf+CAJCgB2NALIyz/6Uswhb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU2BvgVj6UgGk5AHJAqHkMDEzzwsH9ADJ7VQB/jhfBjKCAKpQXccF8vTTP9RvAAHTB/QFkyBus44Q0PQEmvpIUERvjCPHABTmMOgwIW+IuvKJcMjLP8nIz5JPlu8WFMs/Js8LPyLPFCFviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTYG+BWPpSAaTkAckCoeQwMQLLB/QAE8zPgckLAPYwVHqYU6nwAo5rbwAHyPpSUAb6AhT0AFj6Ass/yx/MycjPkVbQSuYmzws/JM8UI2+Ic21UciGpBo4bAcj0AFMhtghRIqEimVOAb4FY+lIBpOQByQKh5DAxNc8LBxP0ABLMycjPhQhSQPpScc8LbszJgED7AH/gbGHQxwAAJsjPhQgU+lJxzwtuE8zJgED7AH8CASAODwBtvOyHaiaGmf/SQY6hi3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjAICchARAgEgEhMAXqlfjQjbGluay5jaGFpbi50b24uY2NpcC5Db250ZXh0RXhlY3V0b3KCLUwLjEuMIAGyoWu1E0NM/MfpIMdQxbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidEAbbSjvaiaGmfmP0kahi3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjAAbbQBfaiaGmfmP0kGOo3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjA='); static Errors = { 'ContextExecutor_Error.OnlyCallableByOwner': 43600, diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index 415a9256c..60018379d 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -11,6 +11,8 @@ import { beginCell, ContractProvider, Sender, SendMode } from '@ton/core'; type RemainingBitsAndRefs = c.Slice +type array = T[] + // TypeScript wrappers flatten a TVM linked list `[1 [2 [3 null]]]` to `[1 2 3]` type lisp_list = T[] @@ -106,6 +108,35 @@ function storeTolkNullable(v: T | null, b: c.Builder, storeFn_T: StoreCallbac } } +function storeArrayOf(v: array, b: c.Builder, storeFn_T: StoreCallback): void { + // the compiler stores array in chunks; in TypeScript, for simplicity, store "1 elem = 1 ref" + let tail = null as c.Cell | null; + for (let i = 0; i < v.length; ++i) { + let chunkB = beginCell().storeMaybeRef(tail); + storeFn_T(v[v.length - 1 - i], chunkB); + tail = chunkB.endCell(); + } + b.storeUint(v.length, 8); + b.storeMaybeRef(tail); +} + +function loadArrayOf(s: c.Slice, loadFn_T: LoadCallback): array { + let len = s.loadUint(8); + let head = s.loadMaybeRef(); + let outArr = [] as array; + while (head != null) { + let s = head.beginParse(); + head = s.loadMaybeRef(); + while (s.remainingBits || s.remainingRefs) { + outArr.push(loadFn_T(s)); + } + } + if (len !== outArr.length) { + throw new Error(`mismatch array binary data: expected ${len} elements, got ${outArr.length}`); + } + return outArr; +} + function storeLispListOf(v: lisp_list, b: c.Builder, storeFn_T: StoreCallback): void { let tail = c.Cell.EMPTY; for (let i = 0; i < v.length; ++i) { @@ -278,6 +309,8 @@ function invokeCustomUnpackFromSlice(typeName: string, s: c.Slice): T { type coins = bigint +type int32 = bigint + type uint8 = bigint type uint16 = bigint type uint32 = bigint @@ -286,6 +319,34 @@ type uint120 = bigint type uint128 = bigint type uint256 = bigint +type varuint32 = bigint + +/** + > type ExtraCurrenciesMap = map + */ +export type ExtraCurrenciesMap = Map + +export const ExtraCurrenciesMap = { + fromSlice(s: c.Slice): ExtraCurrenciesMap { + return dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + ), s)); + }, + store(self: ExtraCurrenciesMap, b: c.Builder): void { + b.storeDict(mapToDict(self, c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )), c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )); + }, + toCell(self: ExtraCurrenciesMap): c.Cell { + return makeCellFrom(self, ExtraCurrenciesMap.store); + } +} + /** > type SnakedCell = cell */ @@ -658,44 +719,6 @@ export const InternalTransferStep = { } } -/** - > struct (0xd53276db) ReturnExcessesBack { - > queryId: uint64 - > } - */ -export interface ReturnExcessesBack { - readonly $: 'ReturnExcessesBack' - queryId: uint64 -} - -export const ReturnExcessesBack = { - PREFIX: 0xd53276db, - - create(args: { - queryId?: uint64 - }): ReturnExcessesBack { - return { - $: 'ReturnExcessesBack', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): ReturnExcessesBack { - loadAndCheckPrefix32(s, 0xd53276db, 'ReturnExcessesBack'); - return { - $: 'ReturnExcessesBack', - queryId: s.loadUintBig(64), - } - }, - store(self: ReturnExcessesBack, b: c.Builder): void { - b.storeUint(0xd53276db, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: ReturnExcessesBack): c.Cell { - return makeCellFrom(self, ReturnExcessesBack.store); - } -} - /** > struct (0x595f07bc) AskToBurn { > queryId: uint64 @@ -842,6 +865,44 @@ export const ClaimMinterAdmin = { } } +/** + > struct JettonClient { + > masterAddress: address + > jettonWalletCode: cell + > } + */ +export interface JettonClient { + readonly $: 'JettonClient' + masterAddress: c.Address + jettonWalletCode: c.Cell +} + +export const JettonClient = { + create(args: { + masterAddress: c.Address + jettonWalletCode: c.Cell + }): JettonClient { + return { + $: 'JettonClient', + ...args + } + }, + fromSlice(s: c.Slice): JettonClient { + return { + $: 'JettonClient', + masterAddress: s.loadAddress(), + jettonWalletCode: s.loadRef(), + } + }, + store(self: JettonClient, b: c.Builder): void { + b.storeAddress(self.masterAddress); + b.storeRef(self.jettonWalletCode); + }, + toCell(self: JettonClient): c.Cell { + return makeCellFrom(self, JettonClient.store); + } +} + /** > type CrossChainAddress = slice */ @@ -859,6 +920,101 @@ export const CrossChainAddress = { } } +/** + > struct (0x55b412b9) ContextExecutor_ForwardNotification { + > id: uint64 + > context: Cell + > forwardFrom: array
+ > message: Cell + > } + */ +export interface ContextExecutor_ForwardNotification { + readonly $: 'ContextExecutor_ForwardNotification' + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward +} + +export const ContextExecutor_ForwardNotification = { + PREFIX: 0x55b412b9, + + create(args: { + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward + }): ContextExecutor_ForwardNotification { + return { + $: 'ContextExecutor_ForwardNotification', + ...args + } + }, +} + +/** + > struct ContextExecutor_InMessageForward { + > senderAddress: address + > valueCoins: coins + > valueExtra: ExtraCurrenciesMap + > originalForwardFee: coins + > createdLt: uint64 + > createdAt: uint32 + > body: cell + > } + */ +export interface ContextExecutor_InMessageForward { + readonly $: 'ContextExecutor_InMessageForward' + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell +} + +export const ContextExecutor_InMessageForward = { + create(args: { + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell + }): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + ...args + } + }, + fromSlice(s: c.Slice): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + senderAddress: s.loadAddress(), + valueCoins: s.loadCoins(), + valueExtra: ExtraCurrenciesMap.fromSlice(s), + originalForwardFee: s.loadCoins(), + createdLt: s.loadUintBig(64), + createdAt: s.loadUintBig(32), + body: s.loadRef(), + } + }, + store(self: ContextExecutor_InMessageForward, b: c.Builder): void { + b.storeAddress(self.senderAddress); + b.storeCoins(self.valueCoins); + ExtraCurrenciesMap.store(self.valueExtra, b); + b.storeCoins(self.originalForwardFee); + b.storeUint(self.createdLt, 64); + b.storeUint(self.createdAt, 32); + b.storeRef(self.body); + }, + toCell(self: ContextExecutor_InMessageForward): c.Cell { + return makeCellFrom(self, ContextExecutor_InMessageForward.store); + } +} + /** > struct CursedSubjects { > data: map @@ -4432,110 +4588,131 @@ export const BurnMintTokenPool_ClaimMinterAdmin = { } /** - > struct BurnMintTokenPool_PendingBurn { - > forwardPayload: TokenPool_LockOrBurnForwardPayload - > expectedSender: address + > struct (0x0d49d523) BurnMintTokenPool_BurnContext { + > wallet: address + > forwardPayload: Cell > } */ -export interface BurnMintTokenPool_PendingBurn { - readonly $: 'BurnMintTokenPool_PendingBurn' +export interface BurnMintTokenPool_BurnContext { + readonly $: 'BurnMintTokenPool_BurnContext' + wallet: c.Address forwardPayload: TokenPool_LockOrBurnForwardPayload - expectedSender: c.Address } -export const BurnMintTokenPool_PendingBurn = { +export const BurnMintTokenPool_BurnContext = { + PREFIX: 0x0d49d523, + create(args: { + wallet: c.Address forwardPayload: TokenPool_LockOrBurnForwardPayload - expectedSender: c.Address - }): BurnMintTokenPool_PendingBurn { + }): BurnMintTokenPool_BurnContext { return { - $: 'BurnMintTokenPool_PendingBurn', + $: 'BurnMintTokenPool_BurnContext', ...args } }, - fromSlice(s: c.Slice): BurnMintTokenPool_PendingBurn { + fromSlice(s: c.Slice): BurnMintTokenPool_BurnContext { + loadAndCheckPrefix32(s, 0x0d49d523, 'BurnMintTokenPool_BurnContext'); return { - $: 'BurnMintTokenPool_PendingBurn', - forwardPayload: TokenPool_LockOrBurnForwardPayload.fromSlice(s), - expectedSender: s.loadAddress(), + $: 'BurnMintTokenPool_BurnContext', + wallet: s.loadAddress(), + forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), } }, - store(self: BurnMintTokenPool_PendingBurn, b: c.Builder): void { - TokenPool_LockOrBurnForwardPayload.store(self.forwardPayload, b); - b.storeAddress(self.expectedSender); + store(self: BurnMintTokenPool_BurnContext, b: c.Builder): void { + b.storeUint(0x0d49d523, 32); + b.storeAddress(self.wallet); + storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); }, - toCell(self: BurnMintTokenPool_PendingBurn): c.Cell { - return makeCellFrom(self, BurnMintTokenPool_PendingBurn.store); + toCell(self: BurnMintTokenPool_BurnContext): c.Cell { + return makeCellFrom(self, BurnMintTokenPool_BurnContext.store); } } /** - > struct BurnMintTokenPool_PendingMint { - > replyTo: address? - > request: Cell - > out: Cell - > expectedSender: address + > struct (0x723f6ea1) BurnMintTokenPool_MintContext { + > forwardPayload: Cell > } */ -export interface BurnMintTokenPool_PendingMint { - readonly $: 'BurnMintTokenPool_PendingMint' - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 - expectedSender: c.Address +export interface BurnMintTokenPool_MintContext { + readonly $: 'BurnMintTokenPool_MintContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload } -export const BurnMintTokenPool_PendingMint = { +export const BurnMintTokenPool_MintContext = { + PREFIX: 0x723f6ea1, + create(args: { - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 - expectedSender: c.Address - }): BurnMintTokenPool_PendingMint { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): BurnMintTokenPool_MintContext { return { - $: 'BurnMintTokenPool_PendingMint', + $: 'BurnMintTokenPool_MintContext', ...args } }, - fromSlice(s: c.Slice): BurnMintTokenPool_PendingMint { + fromSlice(s: c.Slice): BurnMintTokenPool_MintContext { + loadAndCheckPrefix32(s, 0x723f6ea1, 'BurnMintTokenPool_MintContext'); return { - $: 'BurnMintTokenPool_PendingMint', - replyTo: s.loadMaybeAddress(), - request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), - out: loadCellRef(s, TokenPool_ReleaseOrMintOutV1.fromSlice), - expectedSender: s.loadAddress(), + $: 'BurnMintTokenPool_MintContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, - store(self: BurnMintTokenPool_PendingMint, b: c.Builder): void { - b.storeAddress(self.replyTo); - storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); - storeCellRef(self.out, b, TokenPool_ReleaseOrMintOutV1.store); - b.storeAddress(self.expectedSender); + store(self: BurnMintTokenPool_MintContext, b: c.Builder): void { + b.storeUint(0x723f6ea1, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, - toCell(self: BurnMintTokenPool_PendingMint): c.Cell { - return makeCellFrom(self, BurnMintTokenPool_PendingMint.store); + toCell(self: BurnMintTokenPool_MintContext): c.Cell { + return makeCellFrom(self, BurnMintTokenPool_MintContext.store); + } +} + +/** + > type BurnMintTokenPool_ContextExecutorContext = BurnMintTokenPool_BurnContext | BurnMintTokenPool_MintContext + */ +export type BurnMintTokenPool_ContextExecutorContext = + | BurnMintTokenPool_BurnContext + | BurnMintTokenPool_MintContext + +export const BurnMintTokenPool_ContextExecutorContext = { + fromSlice(s: c.Slice): BurnMintTokenPool_ContextExecutorContext { + return lookupPrefix(s, 0x0d49d523, 32) ? BurnMintTokenPool_BurnContext.fromSlice(s) : + lookupPrefix(s, 0x723f6ea1, 32) ? BurnMintTokenPool_MintContext.fromSlice(s) : + throwNonePrefixMatch('BurnMintTokenPool_ContextExecutorContext'); + }, + store(self: BurnMintTokenPool_ContextExecutorContext, b: c.Builder): void { + switch (self.$) { + case 'BurnMintTokenPool_BurnContext': + BurnMintTokenPool_BurnContext.store(self, b); + break; + case 'BurnMintTokenPool_MintContext': + BurnMintTokenPool_MintContext.store(self, b); + break; + } + }, + toCell(self: BurnMintTokenPool_ContextExecutorContext): c.Cell { + return makeCellFrom(self, BurnMintTokenPool_ContextExecutorContext.store); } } /** > struct Storage { > poolData: Cell - > pendingBurns: map> - > pendingMints: map> + > contextExecutorCode: cell + > contextExecutorNextId: uint64 > } */ export interface Storage { readonly $: 'Storage' poolData: TokenPool_Data - pendingBurns: Map /* = [] as map> */ - pendingMints: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 } export const Storage = { create(args: { poolData: TokenPool_Data - pendingBurns: Map /* = [] as map> */ - pendingMints: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }): Storage { return { $: 'Storage', @@ -4546,76 +4723,20 @@ export const Storage = { return { $: 'Storage', poolData: loadCellRef(s, TokenPool_Data.fromSlice), - pendingBurns: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingBurn.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingBurn.store) - ), s)), - pendingMints: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingMint.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingMint.store) - ), s)), + contextExecutorCode: s.loadRef(), + contextExecutorNextId: s.loadUintBig(64), } }, store(self: Storage, b: c.Builder): void { storeCellRef(self.poolData, b, TokenPool_Data.store); - b.storeDict(mapToDict(self.pendingBurns, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingBurn.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingBurn.store) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingBurn.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingBurn.store) - )); - b.storeDict(mapToDict(self.pendingMints, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingMint.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingMint.store) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, BurnMintTokenPool_PendingMint.fromSlice), - (v,b) => storeCellRef(v, b, BurnMintTokenPool_PendingMint.store) - )); + b.storeRef(self.contextExecutorCode); + b.storeUint(self.contextExecutorNextId, 64); }, toCell(self: Storage): c.Cell { return makeCellFrom(self, Storage.store); } } -/** - > struct JettonClient { - > masterAddress: address - > jettonWalletCode: cell - > } - */ -export interface JettonClient { - readonly $: 'JettonClient' - masterAddress: c.Address - jettonWalletCode: c.Cell -} - -export const JettonClient = { - create(args: { - masterAddress: c.Address - jettonWalletCode: c.Cell - }): JettonClient { - return { - $: 'JettonClient', - ...args - } - }, - fromSlice(s: c.Slice): JettonClient { - return { - $: 'JettonClient', - masterAddress: s.loadAddress(), - jettonWalletCode: s.loadRef(), - } - }, - store(self: JettonClient, b: c.Builder): void { - b.storeAddress(self.masterAddress); - b.storeRef(self.jettonWalletCode); - }, - toCell(self: JettonClient): c.Cell { - return makeCellFrom(self, JettonClient.store); - } -} - /** > struct RateLimiter_Config { > isEnabled: bool @@ -4751,7 +4872,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4774,11 +4895,8 @@ export class BurnMintTokenPool implements c.Contract { 'RateLimiter_Error.BucketOverfilled': 26300, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, 'RateLimiter_Error.TokenRateLimitReached': 26302, - 'BurnMintTokenPool_Error.PendingBurnAlreadyExists': 41200, - 'BurnMintTokenPool_Error.PendingMintAlreadyExists': 41202, - 'BurnMintTokenPool_Error.PendingMintNotFound': 41203, - 'BurnMintTokenPool_Error.UnexpectedBurnConfirmationSender': 41204, - 'BurnMintTokenPool_Error.UnexpectedMintConfirmationSender': 41205, + 'BurnMintTokenPool_Error.UnexpectedBurnBounce': 41200, + 'BurnMintTokenPool_Error.UnexpectedMintBounce': 41201, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, @@ -4809,8 +4927,8 @@ export class BurnMintTokenPool implements c.Contract { static fromStorage(emptyStorage: { poolData: TokenPool_Data - pendingBurns: Map /* = [] as map> */ - pendingMints: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }, deployedOptions?: DeployedAddrOptions) { const initialState = { code: deployedOptions?.overrideContractCode ?? BurnMintTokenPool.CodeCell, @@ -4965,10 +5083,21 @@ export class BurnMintTokenPool implements c.Contract { return BurnMintTokenPool_ClaimMinterAdmin.toCell(BurnMintTokenPool_ClaimMinterAdmin.create(body)); } - static createCellOfReturnExcessesBack(body: { - queryId?: uint64 + static createCellOfContextExecutorForwardNotificationBurnMintTokenPoolContextExecutorContext_(body: { + id: uint64 + context: BurnMintTokenPool_ContextExecutorContext + forwardFrom: array + message: ContextExecutor_InMessageForward }) { - return ReturnExcessesBack.toCell(ReturnExcessesBack.create(body)); + return makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, BurnMintTokenPool_ContextExecutorContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ); } async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { @@ -5200,12 +5329,23 @@ export class BurnMintTokenPool implements c.Contract { }); } - async sendReturnExcessesBack(provider: ContractProvider, via: Sender, msgValue: coins, body: { - queryId?: uint64 + async sendContextExecutorForwardNotificationBurnMintTokenPoolContextExecutorContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + id: uint64 + context: BurnMintTokenPool_ContextExecutorContext + forwardFrom: array + message: ContextExecutor_InMessageForward }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, - body: ReturnExcessesBack.toCell(ReturnExcessesBack.create(body)), + body: makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, BurnMintTokenPool_ContextExecutorContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ), ...extraOptions }); } @@ -5256,20 +5396,6 @@ export class BurnMintTokenPool implements c.Contract { ); } - async getHasPendingBurn(provider: ContractProvider, queryId: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('hasPendingBurn', [ - { type: 'int', value: queryId }, - ])); - return r.readBoolean(); - } - - async getHasPendingMint(provider: ContractProvider, queryId: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('hasPendingMint', [ - { type: 'int', value: queryId }, - ])); - return r.readBoolean(); - } - async getRMNProxy(provider: ContractProvider): Promise { const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readSlice().loadAddress(); diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 3754f7ca0..481b4d28b 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -511,6 +511,44 @@ export const ReturnExcessesBack = { } } +/** + > struct JettonClient { + > masterAddress: address + > jettonWalletCode: cell + > } + */ +export interface JettonClient { + readonly $: 'JettonClient' + masterAddress: c.Address + jettonWalletCode: c.Cell +} + +export const JettonClient = { + create(args: { + masterAddress: c.Address + jettonWalletCode: c.Cell + }): JettonClient { + return { + $: 'JettonClient', + ...args + } + }, + fromSlice(s: c.Slice): JettonClient { + return { + $: 'JettonClient', + masterAddress: s.loadAddress(), + jettonWalletCode: s.loadRef(), + } + }, + store(self: JettonClient, b: c.Builder): void { + b.storeAddress(self.masterAddress); + b.storeRef(self.jettonWalletCode); + }, + toCell(self: JettonClient): c.Cell { + return makeCellFrom(self, JettonClient.store); + } +} + /** > type CrossChainAddress = slice */ @@ -4424,44 +4462,6 @@ export const JettonLockBox_WithdrawFailed = { } } -/** - > struct JettonClient { - > masterAddress: address - > jettonWalletCode: cell - > } - */ -export interface JettonClient { - readonly $: 'JettonClient' - masterAddress: c.Address - jettonWalletCode: c.Cell -} - -export const JettonClient = { - create(args: { - masterAddress: c.Address - jettonWalletCode: c.Cell - }): JettonClient { - return { - $: 'JettonClient', - ...args - } - }, - fromSlice(s: c.Slice): JettonClient { - return { - $: 'JettonClient', - masterAddress: s.loadAddress(), - jettonWalletCode: s.loadRef(), - } - }, - store(self: JettonClient, b: c.Builder): void { - b.storeAddress(self.masterAddress); - b.storeRef(self.jettonWalletCode); - }, - toCell(self: JettonClient): c.Cell { - return makeCellFrom(self, JettonClient.store); - } -} - /** > struct Ownable2Step { > owner: address @@ -4719,7 +4719,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 56cf49a49..598545901 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -11,6 +11,8 @@ import { beginCell, ContractProvider, Sender, SendMode } from '@ton/core'; type RemainingBitsAndRefs = c.Slice +type array = T[] + // TypeScript wrappers flatten a TVM linked list `[1 [2 [3 null]]]` to `[1 2 3]` type lisp_list = T[] @@ -106,6 +108,35 @@ function storeTolkNullable(v: T | null, b: c.Builder, storeFn_T: StoreCallbac } } +function storeArrayOf(v: array, b: c.Builder, storeFn_T: StoreCallback): void { + // the compiler stores array in chunks; in TypeScript, for simplicity, store "1 elem = 1 ref" + let tail = null as c.Cell | null; + for (let i = 0; i < v.length; ++i) { + let chunkB = beginCell().storeMaybeRef(tail); + storeFn_T(v[v.length - 1 - i], chunkB); + tail = chunkB.endCell(); + } + b.storeUint(v.length, 8); + b.storeMaybeRef(tail); +} + +function loadArrayOf(s: c.Slice, loadFn_T: LoadCallback): array { + let len = s.loadUint(8); + let head = s.loadMaybeRef(); + let outArr = [] as array; + while (head != null) { + let s = head.beginParse(); + head = s.loadMaybeRef(); + while (s.remainingBits || s.remainingRefs) { + outArr.push(loadFn_T(s)); + } + } + if (len !== outArr.length) { + throw new Error(`mismatch array binary data: expected ${len} elements, got ${outArr.length}`); + } + return outArr; +} + function storeLispListOf(v: lisp_list, b: c.Builder, storeFn_T: StoreCallback): void { let tail = c.Cell.EMPTY; for (let i = 0; i < v.length; ++i) { @@ -278,6 +309,8 @@ function invokeCustomUnpackFromSlice(typeName: string, s: c.Slice): T { type coins = bigint +type int32 = bigint + type uint8 = bigint type uint16 = bigint type uint32 = bigint @@ -286,6 +319,34 @@ type uint120 = bigint type uint128 = bigint type uint256 = bigint +type varuint32 = bigint + +/** + > type ExtraCurrenciesMap = map + */ +export type ExtraCurrenciesMap = Map + +export const ExtraCurrenciesMap = { + fromSlice(s: c.Slice): ExtraCurrenciesMap { + return dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + ), s)); + }, + store(self: ExtraCurrenciesMap, b: c.Builder): void { + b.storeDict(mapToDict(self, c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )), c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )); + }, + toCell(self: ExtraCurrenciesMap): c.Cell { + return makeCellFrom(self, ExtraCurrenciesMap.store); + } +} + /** > type SnakedCell = cell */ @@ -595,44 +656,6 @@ export const TransferNotificationForRecipient = { } } -/** - > struct (0xd53276db) ReturnExcessesBack { - > queryId: uint64 - > } - */ -export interface ReturnExcessesBack { - readonly $: 'ReturnExcessesBack' - queryId: uint64 -} - -export const ReturnExcessesBack = { - PREFIX: 0xd53276db, - - create(args: { - queryId?: uint64 - }): ReturnExcessesBack { - return { - $: 'ReturnExcessesBack', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): ReturnExcessesBack { - loadAndCheckPrefix32(s, 0xd53276db, 'ReturnExcessesBack'); - return { - $: 'ReturnExcessesBack', - queryId: s.loadUintBig(64), - } - }, - store(self: ReturnExcessesBack, b: c.Builder): void { - b.storeUint(0xd53276db, 32); - b.storeUint(self.queryId, 64); - }, - toCell(self: ReturnExcessesBack): c.Cell { - return makeCellFrom(self, ReturnExcessesBack.store); - } -} - /** > struct JettonClient { > masterAddress: address @@ -688,6 +711,101 @@ export const CrossChainAddress = { } } +/** + > struct (0x55b412b9) ContextExecutor_ForwardNotification { + > id: uint64 + > context: Cell + > forwardFrom: array
+ > message: Cell + > } + */ +export interface ContextExecutor_ForwardNotification { + readonly $: 'ContextExecutor_ForwardNotification' + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward +} + +export const ContextExecutor_ForwardNotification = { + PREFIX: 0x55b412b9, + + create(args: { + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward + }): ContextExecutor_ForwardNotification { + return { + $: 'ContextExecutor_ForwardNotification', + ...args + } + }, +} + +/** + > struct ContextExecutor_InMessageForward { + > senderAddress: address + > valueCoins: coins + > valueExtra: ExtraCurrenciesMap + > originalForwardFee: coins + > createdLt: uint64 + > createdAt: uint32 + > body: cell + > } + */ +export interface ContextExecutor_InMessageForward { + readonly $: 'ContextExecutor_InMessageForward' + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell +} + +export const ContextExecutor_InMessageForward = { + create(args: { + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell + }): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + ...args + } + }, + fromSlice(s: c.Slice): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + senderAddress: s.loadAddress(), + valueCoins: s.loadCoins(), + valueExtra: ExtraCurrenciesMap.fromSlice(s), + originalForwardFee: s.loadCoins(), + createdLt: s.loadUintBig(64), + createdAt: s.loadUintBig(32), + body: s.loadRef(), + } + }, + store(self: ContextExecutor_InMessageForward, b: c.Builder): void { + b.storeAddress(self.senderAddress); + b.storeCoins(self.valueCoins); + ExtraCurrenciesMap.store(self.valueExtra, b); + b.storeCoins(self.originalForwardFee); + b.storeUint(self.createdLt, 64); + b.storeUint(self.createdAt, 32); + b.storeRef(self.body); + }, + toCell(self: ContextExecutor_InMessageForward): c.Cell { + return makeCellFrom(self, ContextExecutor_InMessageForward.store); + } +} + /** > struct CursedSubjects { > data: map @@ -4223,16 +4341,33 @@ export const TokenPool_RampAccessUpdated = { } /** - > type LockReleaseTokenPool_InMessage = ReturnExcessesBack + > type LockReleaseTokenPool_InMessage = ContextExecutor_ForwardNotification */ -export type LockReleaseTokenPool_InMessage = ReturnExcessesBack +export type LockReleaseTokenPool_InMessage = ContextExecutor_ForwardNotification export const LockReleaseTokenPool_InMessage = { fromSlice(s: c.Slice): LockReleaseTokenPool_InMessage { - return ReturnExcessesBack.fromSlice(s); + return (() => { + loadAndCheckPrefix32(s, 0x55b412b9, 'ContextExecutor_ForwardNotification'); + return { + $: 'ContextExecutor_ForwardNotification', + id: s.loadUintBig(64), + context: loadCellRef(s, LockReleaseTokenPool_ReleaseContext.fromSlice), + forwardFrom: loadArrayOf(s, + (s) => s.loadAddress() + ), + message: loadCellRef(s, ContextExecutor_InMessageForward.fromSlice), + } + })(); }, store(self: LockReleaseTokenPool_InMessage, b: c.Builder): void { - ReturnExcessesBack.store(self, b); + b.storeUint(0x55b412b9, 32); + b.storeUint(self.id, 64); + storeCellRef(self.context, b, LockReleaseTokenPool_ReleaseContext.store); + storeArrayOf(self.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(self.message, b, ContextExecutor_InMessageForward.store); }, toCell(self: LockReleaseTokenPool_InMessage): c.Cell { return makeCellFrom(self, LockReleaseTokenPool_InMessage.store); @@ -4240,69 +4375,61 @@ export const LockReleaseTokenPool_InMessage = { } /** - > struct LockReleaseTokenPool_PendingRelease { - > replyTo: address? - > request: Cell - > out: Cell - > expectedSender: address + > struct (0x54e6870c) LockReleaseTokenPool_ReleaseContext { + > forwardPayload: Cell > } */ -export interface LockReleaseTokenPool_PendingRelease { - readonly $: 'LockReleaseTokenPool_PendingRelease' - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 - expectedSender: c.Address +export interface LockReleaseTokenPool_ReleaseContext { + readonly $: 'LockReleaseTokenPool_ReleaseContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload } -export const LockReleaseTokenPool_PendingRelease = { +export const LockReleaseTokenPool_ReleaseContext = { + PREFIX: 0x54e6870c, + create(args: { - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 - expectedSender: c.Address - }): LockReleaseTokenPool_PendingRelease { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): LockReleaseTokenPool_ReleaseContext { return { - $: 'LockReleaseTokenPool_PendingRelease', + $: 'LockReleaseTokenPool_ReleaseContext', ...args } }, - fromSlice(s: c.Slice): LockReleaseTokenPool_PendingRelease { + fromSlice(s: c.Slice): LockReleaseTokenPool_ReleaseContext { + loadAndCheckPrefix32(s, 0x54e6870c, 'LockReleaseTokenPool_ReleaseContext'); return { - $: 'LockReleaseTokenPool_PendingRelease', - replyTo: s.loadMaybeAddress(), - request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), - out: loadCellRef(s, TokenPool_ReleaseOrMintOutV1.fromSlice), - expectedSender: s.loadAddress(), + $: 'LockReleaseTokenPool_ReleaseContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, - store(self: LockReleaseTokenPool_PendingRelease, b: c.Builder): void { - b.storeAddress(self.replyTo); - storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); - storeCellRef(self.out, b, TokenPool_ReleaseOrMintOutV1.store); - b.storeAddress(self.expectedSender); + store(self: LockReleaseTokenPool_ReleaseContext, b: c.Builder): void { + b.storeUint(0x54e6870c, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, - toCell(self: LockReleaseTokenPool_PendingRelease): c.Cell { - return makeCellFrom(self, LockReleaseTokenPool_PendingRelease.store); + toCell(self: LockReleaseTokenPool_ReleaseContext): c.Cell { + return makeCellFrom(self, LockReleaseTokenPool_ReleaseContext.store); } } /** > struct Storage { > poolData: Cell - > pendingReleases: map> + > contextExecutorCode: cell + > contextExecutorNextId: uint64 > } */ export interface Storage { readonly $: 'Storage' poolData: TokenPool_Data - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 } export const Storage = { create(args: { poolData: TokenPool_Data - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }): Storage { return { $: 'Storage', @@ -4313,21 +4440,14 @@ export const Storage = { return { $: 'Storage', poolData: loadCellRef(s, TokenPool_Data.fromSlice), - pendingReleases: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseTokenPool_PendingRelease.store) - ), s)), + contextExecutorCode: s.loadRef(), + contextExecutorNextId: s.loadUintBig(64), } }, store(self: Storage, b: c.Builder): void { storeCellRef(self.poolData, b, TokenPool_Data.store); - b.storeDict(mapToDict(self.pendingReleases, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseTokenPool_PendingRelease.store) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseTokenPool_PendingRelease.store) - )); + b.storeRef(self.contextExecutorCode); + b.storeUint(self.contextExecutorNextId, 64); }, toCell(self: Storage): c.Cell { return makeCellFrom(self, Storage.store); @@ -4469,7 +4589,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4489,13 +4609,10 @@ export class LockReleaseTokenPool implements c.Contract { 'TokenPool_Error.OverflowDetected': 14914, 'TokenPool_Error.UnsupportedOperation': 14917, 'TokenPool_Error.InvalidRequestedFinality': 14921, - 'Error.PendingReleaseAlreadyExists': 26300, + 'Error.UnexpectedReleaseBounce': 26300, 'RateLimiter_Error.BucketOverfilled': 26300, - 'Error.PendingReleaseNotFound': 26301, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, - 'Error.UnexpectedReleaseConfirmationSender': 26302, 'RateLimiter_Error.TokenRateLimitReached': 26302, - 'Error.UnexpectedReleaseBounce': 26303, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, @@ -4526,7 +4643,8 @@ export class LockReleaseTokenPool implements c.Contract { static fromStorage(emptyStorage: { poolData: TokenPool_Data - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }, deployedOptions?: DeployedAddrOptions) { const initialState = { code: deployedOptions?.overrideContractCode ?? LockReleaseTokenPool.CodeCell, @@ -4952,13 +5070,6 @@ export class LockReleaseTokenPool implements c.Contract { ); } - async getHasPendingRelease(provider: ContractProvider, queryId: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('hasPendingRelease', [ - { type: 'int', value: queryId }, - ])); - return r.readBoolean(); - } - async getRMNProxy(provider: ContractProvider): Promise { const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readSlice().loadAddress(); From ebd58f44f208892b7f60cb9c4aa0044963fd1887 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Thu, 30 Jul 2026 12:23:49 +0200 Subject: [PATCH 02/11] Stash current behaviour - throw on error in transfer continuation --- .../ccip/pools/burn_mint/contract.tolk | 39 +++++- .../ccip/pools/burn_mint/errors.tolk | 1 + .../ccip/pools/burn_mint/storage.tolk | 6 +- .../ccip/pools/lock_release/contract.tolk | 7 +- .../ccip/pools/lock_release/errors.tolk | 1 + .../ccip/pools/lock_release/storage.tolk | 6 +- .../ccip/pools/BurnMintTokenPool.spec.ts | 109 +++++++++++------ .../ccip/pools/LockReleaseTokenPool.spec.ts | 113 +++++++++--------- .../gen/ccip/pools/BurnMintTokenPool.ts | 3 +- .../gen/ccip/pools/LockReleaseTokenPool.ts | 3 +- 10 files changed, 192 insertions(+), 96 deletions(-) diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 69c5925f4..83194b511 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -204,6 +204,20 @@ fun onReleaseOrMintContinue( return st; } +fun onLockOrBurnTransfer( + st: Storage, + _sender: address, + _msg: TransferNotificationForRecipient, +): Storage { + assert(st.canTakeContextExecutorNextId()) throw BurnMintTokenPool_Error.ContextExecutorUnavailable; + return st; +} + +fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { + assert(st.canTakeContextExecutorNextId()) throw BurnMintTokenPool_Error.ContextExecutorUnavailable; + return st; +} + fun refundLockOrBurnRateLimit( mutate st: Storage, forwardPayload: TokenPool_LockOrBurnForwardPayload, @@ -257,6 +271,27 @@ fun sendLockOrBurnFailure( } } +fun returnLockOrBurnTransfer(sender: address, forwardPayload: TokenPool_LockOrBurnForwardPayload) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val returnTransfer = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: sender, + body: AskToTransfer { + queryId: requestMsg.queryId, + jettonAmount: details.amount, + transferRecipient: details.originalSender, + sendExcessesTo: details.originalSender, + customPayload: null, + forwardTonAmount: 0, + forwardPayload: beginCell().storeBool(false).endCell().beginParse(), + }, + }); + returnTransfer.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { val requestMsg = lazy forwardPayload.requestMsg.load(); val request = lazy requestMsg.request.load(); @@ -372,9 +407,9 @@ fun loadPool(st: Storage): TokenPool { onLockOrBurn: null, validateLockOrBurn: null, preflightCheck: null, - onLockOrBurnTransfer: null, + onLockOrBurnTransfer, onLockOrBurnTransferContinue, - onReleaseOrMint: null, + onReleaseOrMint, validateReleaseOrMint: null, postflightCheck: null, onReleaseOrMintContinue, diff --git a/contracts/contracts/ccip/pools/burn_mint/errors.tolk b/contracts/contracts/ccip/pools/burn_mint/errors.tolk index 7eae24735..4aff79476 100644 --- a/contracts/contracts/ccip/pools/burn_mint/errors.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/errors.tolk @@ -6,4 +6,5 @@ const BurnMintTokenPool_FACILITY_ID = 412; // (crc32() % 640) + 10 enum BurnMintTokenPool_Error { UnexpectedBurnBounce = BurnMintTokenPool_FACILITY_ID * 100 UnexpectedMintBounce + ContextExecutorUnavailable } diff --git a/contracts/contracts/ccip/pools/burn_mint/storage.tolk b/contracts/contracts/ccip/pools/burn_mint/storage.tolk index 9c8461790..38828bc08 100644 --- a/contracts/contracts/ccip/pools/burn_mint/storage.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/storage.tolk @@ -12,10 +12,14 @@ struct Storage { contextExecutorNextId: uint64; } +@inline +fun Storage.canTakeContextExecutorNextId(self): bool { + return self.contextExecutorNextId != 18446744073709551615; +} + @inline fun Storage.takeContextExecutorNextId(mutate self): uint64 { val id = self.contextExecutorNextId; - assert(id != 0) throw 0xFFFF; assert(id != 18446744073709551615) throw 0xFFFF; self.contextExecutorNextId = id + 1; return id; diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index 0c24c3a39..4c8e2d804 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -138,6 +138,11 @@ fun onReleaseOrMintContinue( return st; } +fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { + assert(st.canTakeContextExecutorNextId()) throw Error.ContextExecutorUnavailable; + return st; +} + fun refundReleaseOrMintRateLimit( mutate st: Storage, forwardPayload: TokenPool_ReleaseOrMintForwardPayload, @@ -256,7 +261,7 @@ fun loadPool(st: Storage): TokenPool { preflightCheck: null, onLockOrBurnTransfer: null, onLockOrBurnTransferContinue: null, - onReleaseOrMint: null, + onReleaseOrMint, validateReleaseOrMint: null, postflightCheck: null, onReleaseOrMintContinue, diff --git a/contracts/contracts/ccip/pools/lock_release/errors.tolk b/contracts/contracts/ccip/pools/lock_release/errors.tolk index 6a4e92d06..4992673e1 100644 --- a/contracts/contracts/ccip/pools/lock_release/errors.tolk +++ b/contracts/contracts/ccip/pools/lock_release/errors.tolk @@ -5,4 +5,5 @@ const LockReleaseTokenPool_FACILITY_ID = 263; // (crc32() % 640) + 10 enum Error { UnexpectedReleaseBounce = LockReleaseTokenPool_FACILITY_ID * 100 + ContextExecutorUnavailable } diff --git a/contracts/contracts/ccip/pools/lock_release/storage.tolk b/contracts/contracts/ccip/pools/lock_release/storage.tolk index 0d5386834..6c0886b15 100644 --- a/contracts/contracts/ccip/pools/lock_release/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release/storage.tolk @@ -12,10 +12,14 @@ struct Storage { contextExecutorNextId: uint64; } +@inline +fun Storage.canTakeContextExecutorNextId(self): bool { + return self.contextExecutorNextId != 18446744073709551615; +} + @inline fun Storage.takeContextExecutorNextId(mutate self): uint64 { val id = self.contextExecutorNextId; - assert(id != 0) throw 0xFFFF; assert(id != 18446744073709551615) throw 0xFFFF; self.contextExecutorNextId = id + 1; return id; diff --git a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts index c7fbd475e..69e8c5bdd 100644 --- a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts @@ -384,45 +384,85 @@ describe('BurnMintTokenPool', () => { }) }) - it('reverts lockOrBurn when payload amount does not match transferred amount', async () => { - const onRampWallet = await userWallet(deployer.address) - const poolWallet = await userWallet(burnMintPool.address) - const result = await onRampWallet.sendTransfer(deployer.getSender(), { - value: toNano('2'), - message: { - queryId: 304, - jettonAmount: toNano('2'), - destination: burnMintPool.address, - responseDestination: deployer.address, - customPayload: null, - forwardTonAmount: toNano('0.2'), - forwardPayload: TokenPool_LockOrBurn.toCell( - TokenPool_LockOrBurn.create({ - queryId: 304n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 304n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('1'), - localToken: cctMinter.address, + describe('lockOrBurn transfer input validation (current behavior)', () => { + it('currently aborts when payload amount does not match transferred amount', async () => { + const onRampWallet = await userWallet(deployer.address) + const poolWallet = await userWallet(burnMintPool.address) + const result = await onRampWallet.sendTransfer(deployer.getSender(), { + value: toNano('2'), + message: { + queryId: 304, + jettonAmount: toNano('2'), + destination: burnMintPool.address, + responseDestination: deployer.address, + customPayload: null, + forwardTonAmount: toNano('0.2'), + forwardPayload: TokenPool_LockOrBurn.toCell( + TokenPool_LockOrBurn.create({ + queryId: 304n, + request: TokenPool_LockOrBurnInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 304n, + details: TokenPool_TransferDetails.create({ + receiver: receiverAddress, + remoteChainSelector, + originalSender: deployer.address, + amount: toNano('1'), + localToken: cctMinter.address, + }), }), }), + requestedFinalityConfig: 0n, + tokenArgs: null, + replyTo: deployer.address, }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }), - ), - }, + ), + }, + }) + + expect(result.transactions).toHaveTransaction({ + from: poolWallet.address, + to: burnMintPool.address, + success: false, + }) }) - expect(result.transactions).toHaveTransaction({ - from: poolWallet.address, - to: burnMintPool.address, - success: false, + it('currently aborts when transfer forward payload is malformed', async () => { + await cctMinterRuntime.sendMint(deployer.getSender(), { + value: toNano('1'), + mintOpcode: 0x00000015, + message: { + queryId: 101n, + destination: deployer.address, + tonAmount: toNano('0.05'), + jettonAmount: toNano('10'), + from: deployer.address, + responseDestination: deployer.address, + forwardTonAmount: 0n, + }, + }) + + const deployerWallet = await userWallet(deployer.address) + const poolWallet = await userWallet(burnMintPool.address) + + const result = await deployerWallet.sendTransfer(deployer.getSender(), { + value: toNano('2'), + message: { + queryId: 45, + jettonAmount: toNano('1'), + destination: burnMintPool.address, + responseDestination: deployer.address, + customPayload: null, + forwardTonAmount: toNano('0.2'), + forwardPayload: beginCell().storeUint(0, 32).endCell(), + }, + }) + + expect(result.transactions).toHaveTransaction({ + from: poolWallet.address, + to: burnMintPool.address, + success: false, + }) }) }) @@ -550,6 +590,7 @@ describe('BurnMintTokenPool', () => { }) }) + it('mints tokens on releaseOrMint path and finalizes through the executor notification', async () => { const result = await burnMintPool.sendTokenPoolReleaseOrMint( offRamp.getSender(), diff --git a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts index 359243490..dcfd14ede 100644 --- a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts @@ -311,68 +311,70 @@ describe('LockReleaseTokenPool', () => { } }) - it('reverts lockOrBurn when forwarded amount does not match transfer amount', async () => { - const onRampWallet = await userWallet(jettonSender.address) - const poolWallet = await userWallet(lockReleasePool.address) - - const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { - value: toNano('2'), - message: { - queryId: 44n, - amount: toNano('3'), - destination: lockReleasePool.address, - customPayload: beginCell().storeBit(1).endCell(), - forwardTonAmount: toNano('0.2'), - forwardPayload: TokenPool_LockOrBurn.toCell( - TokenPool_LockOrBurn.create({ - queryId: 44n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 44n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('2'), - localToken: jettonMinter.address, + describe('lockOrBurn transfer input validation (current behavior)', () => { + it('currently aborts when forwarded amount does not match transfer amount', async () => { + const onRampWallet = await userWallet(jettonSender.address) + const poolWallet = await userWallet(lockReleasePool.address) + + const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { + value: toNano('2'), + message: { + queryId: 44n, + amount: toNano('3'), + destination: lockReleasePool.address, + customPayload: beginCell().storeBit(1).endCell(), + forwardTonAmount: toNano('0.2'), + forwardPayload: TokenPool_LockOrBurn.toCell( + TokenPool_LockOrBurn.create({ + queryId: 44n, + request: TokenPool_LockOrBurnInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 44n, + details: TokenPool_TransferDetails.create({ + receiver: receiverAddress, + remoteChainSelector, + originalSender: deployer.address, + amount: toNano('2'), + localToken: jettonMinter.address, + }), }), }), + requestedFinalityConfig: 0n, + tokenArgs: null, + replyTo: deployer.address, }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }), - ), - }, - }) + ), + }, + }) - expect(result.transactions).toHaveTransaction({ - from: poolWallet.address, - to: lockReleasePool.address, - success: false, + expect(result.transactions).toHaveTransaction({ + from: poolWallet.address, + to: lockReleasePool.address, + success: false, + }) }) - }) - - it('reverts lockOrBurn when forward payload is malformed', async () => { - const onRampWallet = await userWallet(jettonSender.address) - const poolWallet = await userWallet(lockReleasePool.address) - const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { - value: toNano('2'), - message: { - queryId: 45n, - amount: toNano('1'), - destination: lockReleasePool.address, - customPayload: beginCell().storeBit(1).endCell(), - forwardTonAmount: toNano('0.2'), - forwardPayload: beginCell().storeUint(0, 32).endCell(), - }, - }) + it('currently aborts when forward payload is malformed', async () => { + const onRampWallet = await userWallet(jettonSender.address) + const poolWallet = await userWallet(lockReleasePool.address) + + const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { + value: toNano('2'), + message: { + queryId: 45n, + amount: toNano('1'), + destination: lockReleasePool.address, + customPayload: beginCell().storeBit(1).endCell(), + forwardTonAmount: toNano('0.2'), + forwardPayload: beginCell().storeUint(0, 32).endCell(), + }, + }) - expect(result.transactions).toHaveTransaction({ - from: poolWallet.address, - to: lockReleasePool.address, - success: false, + expect(result.transactions).toHaveTransaction({ + from: poolWallet.address, + to: lockReleasePool.address, + success: false, + }) }) }) @@ -574,6 +576,7 @@ describe('LockReleaseTokenPool', () => { }) }) + it('releases tokens with null replyTo without emitting a response message', async () => { const poolWallet = await userWallet(lockReleasePool.address) const recipientWallet = await userWallet(recipient.address) diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index 60018379d..4cdf9489b 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -4872,7 +4872,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4897,6 +4897,7 @@ export class BurnMintTokenPool implements c.Contract { 'RateLimiter_Error.TokenRateLimitReached': 26302, 'BurnMintTokenPool_Error.UnexpectedBurnBounce': 41200, 'BurnMintTokenPool_Error.UnexpectedMintBounce': 41201, + 'BurnMintTokenPool_Error.ContextExecutorUnavailable': 41202, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 598545901..99a1d019b 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -4589,7 +4589,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECtwEALC0AART/APSkE/S88sgLAQIBYgIDAgLLBAUCASCNjgIBIAYHAgEgZmcCASAICQIBICEiAgEgCgsCASAbHAIBIAwNAFdSFukltw4IJpAAAAAAAAAAAAAAAAAAABIoMG9A5voTGSW3/gAYMG9A5voTGAL3PiRjqDtRNDU1NM/0QPXLCf////08r/XTNDXLCB8U/Us4wLyP+DtRNDU1NM/0SLQ1NTTB/QE9ATRgQCFbW1tbW1tkvAJAG1tbW1tgQCGVhVWFVYV+JL4lwQREgQDEREDAhEQAhBPED4QLRBMEDsQKhBJEDgQJxBGEDUQJIA4PAak7aLt+9csJ5Db7QyORNcsJ88U8lSUW3DbMeGCAMKKI26z8vQhggDCigTHBRPy9CBtA9cLP4sCAcjLPxX6UhL6UsnIz4cgFM5xzwthE8zJcPsA4w1/gGgH+0z8x+gAx+kgx+lAx9AX4koFmvCTQ1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kjXTPgoyM+EAvpSEvpSyQHIz4TQzMz5FsjPigBAy//PUBLHBfL0gWa8IW6z8vTQ1ywipzQ4ZPK/10zQ+kgx1NTRIdDXLCGo+78c8r/TPzHU1wsfARABjhAjVhvwClcRXw0DjiQ6OsMAkjU1ljc3EEYQNeICyMzMywcS9AD0AMnIzBLMyz/J7VTgXwgj1ywiraCVzOMCXwSEDwHHAPL0FgLi0NP/MddM0ALQJdA2BdTU0wf0BPQE0Qb6SDHXCz8J0x8x+gAwBeMPAcjMzMsHFPQAE/QAyQLQ1ywhqPu/HPK/0z/THzH6UDAgbpFbjhrIz4UI+lKCEO8Ms27PC47LP8+JmvLJgEH7AOIByMzMyz/J7VQREgL+gTo4U5GAQPQOb6ES8vTU9ATU1NEg0NTU0dDTd9M/0gDTd9N30SKON18GAdDU1NHQ03fTP9IA03fTd9Es0NQx+kgx1DH6SDBS4PAGBMjLdxPLP8oAy3fLd8kByMzMyQHjDQPIzBL0AMzMUpKAQPRDI9DUMfpIMdQx+kgwCcjLPxMUAf6BOjhTkYBA9A5voRLy9NT0BNTU0QHQ1NTR0NN30z/SANN303fRLNDUMfpIMdQx+kgwUuDwBgTIy3cTyz/KAMt3y3fJAcjMzMkDyMwS9AASzMxSkoBA9EMj0NQx+kgx1DH6SDAJyMs/GfpSUAT6AsnIz48YAASCEHQJrY/PC/dxFQBWNizQ1DH6SDHUMfpIMBBFEDRBMFQm4PAGBMjLdxPLP8oAy3fLd8kByMzMyQBAGfpSUAT6AsnIz48YAASCEDR//HzPC/dxzwthzMlw+wAAEM8LYczJcPsAAv40A9M/1G8AAdMH9ASTIW6zjhIB0PQEmvpIUFVvjCTHABXmMAHoMSJviFi68onXTPiS+CgFyMs/FfpSI88UIm+Ic21UciGpBo4bAcj0AFMhtghRIqEimVNwb4FY+lIBpOQByQKh5DAxNM8LBxL0AMlSQIE6PgLIz4TQzMz5FsiJFxgAA4AQAf7PFsv/z1AUxwUT8vQB0PpIMfoAMfQEMfoAMdM/MdMfMdTR0NcsJqmTttzyv9M/0QHQ1ywipzQ4ZPK/1NHQ+kgx1NTRhA8i0NcsIaj7vxzyv9M/1DHTHzH6UDHRFLoT8vTQ1ywhqPu/HPK/0z/U0x8x+lAwAdDT/zHXTNAD0NMfGQDkMfoAMfoAMFIE+kjTP9P/MfpIMPgoA8j6UskByPpSE/pSUAb6AszJyM+PGAAEghDpwAyXzwv3cM8LYRXLPxTMyXD7ACBukl8Djh7IUAP6AsnIz4UIE/pSghDg6IL1zwuOyz/MyYBB+wDiAcjMzMs/ye1UAGZsEtM/+kgwggDCiFE0xwUT8vSCAMKJUyPHBbPy9CGLAsjPhyDOcM8LYRLLPxL6Uslw+wACASAdHgIBIB8gAGkIMJNkzBwceBxepMiwgCOICJxsMABnIT/IqkEIb7yhGaoAd4CqwAgwgCUUSCoAt4C6DAxcIACPDAjs5F/lSDAAMMA4pEw4FNS+CMnoRBoXjQQN0iAUoDwBzVRZbmVgWa98vDgUzS5jhBfBSCVgWa+8vDhMIFmvvLw4FA0oVA0gAFkMCOzkX+VIMAAwwDikTDgU1L4IyehEGheNBA3SIBSgPAHNVBFoFAFtghERAOAANQgjhU2XLyVgWa88vDgUVKooBS2CPgjUETgW4AIBIDIzAgEgIyQCASAlJgIBIC8wAfcJsMAlSpus8MAkXDijihXEwgRFQgHERQHBhETBlUEERIp2pgHERQHBhETBgUREgWBAIURElVA4CPQ0//U0SDQ0z/6SDH6APpIMIE6PVYh0NQx+kgx1DH6SDBYxwXy9IE6OSJWHoBA9A5voTHy9FYfVh9WH1YfVh9WH1YfgJwBpFDNXws3Nzc4BMMAlSJus8MAkXDilzVFFVAz2lDgXwQygTo6AdD0BDH0BDH0BNFY8AOz8vSAB/FYfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YX8A1WHtD0BPQEMfQEMdFSIIBA9A5voZP6SNGSMG3igTo+IW6z8vSBOj5RG8cF8vQqwwCWVhZus8MAkXDinlYaVhpWGlYaU9VWHNpg3lYfAlYfAlYfAlYfAlYfAlYfAigD/FYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAgERGQERGFYc8A5moSXjD4E6OFExgED0Dm+hFPL0AtT0BDHUMdQx0VYcyMv/ycjPk+n2kRIpzws/KM8UJ88LH1Jg9ABSUPpUycgk+gJWHikqKwL+Vh7Q1DH6SDHUMfpIMdQx0x/6UDHRUmDwCIE6OCNWHYBA9A5voRLy9NT0BNTU0SDQ1NTRAdDTd9M/0gDTd9N30SKOOF8GAdDU1NEB0NN30z/SANN303fRVifQ1DH6SDHUMfpIMFKg8AUEyMt3E8s/ygDLd8t3ycjMzMkB4w0DyCwtAf6BOjgjVh2AQPQOb6ES8vTU9ATU1NEB0NTU0QHQ03fTP9IA03fTd9FWJ9DUMfpIMdQx+kgwUqDwBQTIy3cTyz/KAMt3y3fJyMzMyQPIzBL0ABLMzFIyER2AQPRDVh7Q1DH6SDHUMfpIMCPIyz/6UlYc+gLJyM+PGAAEghDPUFn8LgDc+gIjzxQizxTJK1YiCVYiCVYiUZAJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJViIJCBEiCAcRIQcGESAGBREfBRA0ECMBESABER/wDwIRFwJQRAMAVjZWJ9DUMfpIMdQx+kgwEEUQNEEwVCag8AUEyMt3E8s/ygDLd8t3ycjMzMkAfMwS9ADMzFIyER2AQPRDVh7Q1DH6SDHUMfpIMCPIyz/6UlYc+gLJyM+PGAAEghDrpIwLzwv3cc8LYczJcPsAABjPC/dxzwthzMlw+wAAmxXElcQXw8ybDMz0NM/+kgx+gAwAoBA9A5voZNfA3Dh0x8x0x8x+gAx+gAx0w/TD9IA0ZNfBHDhA5EwkTLigTo0IoEnELny9KiBJxCpBIAGVDg5OTo6Ojo6Ojo6Oj4+Pj4DwwCVKW6zwwCRcOKePRB8EGsQWhBJUAcI2sHgbFU1B9DUMfpIMdQx+kgx1DHTHzH6UNEgbuMDXwhwgMQCSJ9D6ADH6ANQx1DHR+CgFyPpSFMwYzMnIz5EEp0QmFcs/zBXLHxP0AFAD+gL6UvQAycjPhYgS+lLPhBBz+gJxzwtlzMmAQPsAfwIBIDQ1AgEgOToAKQhkVvhgTpJIZQCusMAk2whcOLy9IAH1IE6RQnDABny9CrQ1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kjXTCbQ0/8x10zQyM+Q1H3fjinPCz8YzBbLHxT6VMkCyMsfIfoCUAj6AskE+kgwUwLIz4QCEvpS+lLJJMjPhNDMzPkWyM+KAEDL/89QB8j6UhLMFMzJhA8ogNgH+8vSEDyiEP73y9Cek+CjIz5FTmhwyI88UyQhvAQrIyz/6UhfMKG+Ic21UciGpBo4bAcj0AFMhtghRIqEimVPQb4FY+lIBpOQByQKh5DAxOs8LBxj0AMkoggkxLQCLCMjOycjPiQgBU0PIz4TQzMz5Fs8L/1j6AoEAjc8LcCLPFDcB/CPPFMzJcfsA+CjIz4QC+lIT+lLJUAPIz4TQzMz5FsjPigBAy//PUAKCCvrwgALIz4TQzMz5FsjPigBAy//PUMjPkVOaHDIYzMmLBMjPkD4p+pYWyz9QB/oCE/pSFvpUFPQAz4QgzsnIz4WIFPpSWPoCz4Fz+gJxzwtlEszJcTgACvsAgQCFAT07aLt+zEg1ywn0+0iJJ0x0z/U0x/0BPpQMPAL4w5/gOwH1CbDAJUrbrPDAJFw4o4bVxMIERUIBxEUBwYREwZVBBESKtqTT+2BAIUN4BEVERsRFREUERoRFBETERkRExESERgREhERERcREREQERYREA8RGw8OERoODREZDQwRGAwLERcLChEWCgkRGwkIERoIBxEZBwYRGAYFERcFgZAH+1ywgR5f9vI50bCHTPzHXTND6SNTU0QHQAdAB1ywn0+0iJPK/0z/U0x/0BPpQMAX6APoA1NdMyM+T6faREijPCz8XzBXLHxP0AFJg+lTJyFj6Alj6AhLMEszJyM+Tno1BBhPLPxT6UhPMEszJyM+FiBL6UnHPC27MyYBA+wDjDjwD+tcsJTb9MRyPctcsI5sWhOSZMdM/+gD6UPAQj17XLCGo+78cmzHTP9TTH/pQMPASj0jXLCTxU1s0jjNsIdM/MddM0PpI1NTRAdAB0AHXLCGo+78c8r/TP9TTH/pQMATTH/oA+gAwEFYQRRA08BiPCdcsIQ847DzjD+Li4uMNPT4/AvBsIdM/MddM0PpIMdTU0QHQAdAB1ywhqPu/HPK/0z/U0x/6UDAC0NP/MdTUMfQEMfQEMdHQ+kgx0z/UMdP/MfpIMdEE0x8x+gAwAeMPVhRukzBXE44iyM+FCAERFQH6UoIQ7wyzbs8LjgERFAHLP8+J0ebJgED7AOJAQQMo1ywit7npvI8J1ywgvhIW5OMP4w1FRkcC5Gwh0z8x10zQ+kgx1NTRAdAB0AHXLCfT7SIk8r/TP9TTH/QB+lAwAtDT/zHU0dDUMdM/+kgx+gAx+kgx0QT6ADH6ADAB4w9WFG6TMFcTjiLIz4UIAREVAfpSghA0dupyzwuOAREUAcs/z4nR5smAQPsA4l9gAv6BOjgkVhiAQPQOb6ES8vTU9ATU1NEg0NTU0dDTd9M/0gDTd9N30SKOOF8GAdDU1NHQ03fTP9IA03fTd9FWItDUMfpIMdQx+kgwUqDwBgTIy3cTyz/KAMt3y3fJAcjMzMkB4w0DyMwS9ADMzFJCERiAQPRDVhnQ1DH6SDHUMfpIQkMB9oE6OCRWGIBA9A5voRLy9NT0BNTU0QHQ1NTR0NN30z/SANN303fRViLQ1DH6SDHUMfpIMFKg8AYEyMt3E8s/ygDLd8t3yQHIzMzJA8jMEvQAEszMUkIRGIBA9ENWGdDUMfpIMdQx+kgwBMjLPxT6UgERFvoCycjPjxgABEQAWDZWItDUMfpIMdQx+kgwEEUQNEEwVCag8AYEyMt3E8s/ygDLd8t3yQHIzMzJAEwwBMjLPxT6UgERFvoCycjPjxgABIIQNH/8fM8L93HPC2HMyXD7AAAkghB0Ca2Pzwv3cc8LYczJcPsAAfwx0z/TP9dMVhnQ1PpIMdQx+kgx1DHTHzH6UDHR0PpI+lAx0SSCAMKIAscF8vSBOjgiVhiAQPQOb6Ex8vSBOjgiVhiAQPQOb6ES8vTU9ATU1NGBOjcl0NMHIcFB8oUBqgLXGNHXScMA8vQk+QCBOj9TFIMH9A5voTGz8vRURRRIA/LXLCITXGYkj27XLCa7iUCEjuPXLCHihRzcjlgx0z/XCx8RGNDU+kjU+kjU0x8x+lDRJdD6SPpQMdEoggDCiALHBfL0Vh0GyMwV+lITzPpSzBLLH/pUycjPhQgT+lKCEEJqcTvPC47LPwERFwHLH8mAQPsA4w7jDeMNSUpLAv4x0z/U10xWGdDU+kgx1DH6SDHUMdMfMfpQMdHQ+kj6UDHRJIIAwogCxwXy9AHQlCDHALOOPyDXSwGRMJuBNLwBwAHy9NdM0OLTP1IQERiAQPRbgTo4AfL0yM+PGAAEghAnkIKLzwv3cM8LYRLLP8lw+wARFugw0JQgxwCziugwWlsApIMH9BfIz48YAASCEL8NGrbPC/dwzwthJs8LPyXPFMlw+wADyMwT9AASzMxSIhEYgED0Q8jPhQgU+lKCEBLMSYXPC44Syz/LPwERFAHMyYBA+wAD9NcsIfrk+ryPb9csIn8Wk2SO5DHTP9dMVhjQ1PpIMdT6SDHUMdMfMfpQMdHQ+kj6UPpQ0QPQ+kj6UDHRkvAZAFQkcOxPJIE6PgPHBZIwf5TaAcMA4vL00JQgxwCziugwyM+FCBL6UoIQ3XsMcc8Ljss/yYBA+wDjDuMNTE1OAOYx0z/6SPpQ+lAwERrQ1PpI1DH6SNTTH/pQ0SXQ+kj6UDHRKoIAwogCxwXy9CfI+lJScPpUViAB+lTJBsjMFfpSFcwS+lLMEssf+lTJyM+S3NeMMhTLPxL6UvpUAREYAfpUycjPhQgS+lJxzwtuzMmAQPsAAfox0z/TP9dMVhnQ1PpIMdQx+kgx1DHTHzH6UDHR0PpI+lAx0SSCAMKIAscF8vSBOjgiVhiAQPQOb6Ex8vSBOjgiVhiAQPQOb6ES8vTU9ATU1NEk+QBQA4MH9FuBOkAB8vQDyMwT9AASzMxSIhEYgED0Q8jPjxgABIIQvBTH6FkC/CDXSwGRMJuBNLwBwAHy9NdM0OLTP9IA1NSBOjglVhuAQPQOb6ES8vTU9ATU1NEnjj8B0NQx1DHRJdDSANN303fR+CMiyMt3yz8TygDLd8t3ySXQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kByMzMyQHjDQPIzBL0AMzMUlIRG09QA/LXLCGFDo+8j27XLCcYOyX0juPXLCTJTbIUjlgx0z/6SDARGNDU+kgx1PpI1NMf+lDRJdD6SPpQMdEoggDCiALHBfL0Vh0GyMwW+lIUzBL6UszLH/pUycjPhQgT+lKCEOXQiy7PC47LPwERFwH6UsmAQPsA4w7jDeMNUVJTAK4x0z/6UDARGNDU+kjU+kjU0x/6UDHRJdD6SPpQMdEoggDCiALHBfL0Vh0GyMwV+lITzPpSzMsf+lTJyM+FCBP6UoIQPIadgM8Ljss/AREXAfpUyYBA+wAAetDUMdQx0SXQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kl0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAcjMzMkASoBA9EPIz48YAASCEP+dv3bPC/dwzwthFcs/E8oAzMzJcPsAERUB/tcsJO0m0EyOTzBWF9DU+kjU+kjU0x/6UNEG0PpI+lDRQQko8AGOKDdXHREcyPpSFfpUycjMEvpSzBL6UgERGAHMyx8BERYB+lTJERV/2zHgEHhfCMcA2zHhMdM/9AVWGNDU+kjUMfpIMdQx0x8x+lAx0QHQ+kj6UDHRJIE6PgJUAdAx0z/XTFYY0NT6SDHUMfpIMdQx0x8x+lAx0dD6SPpQMdEjggDCiALHBfL0ERfQ9AT0BPQE0REZ0JQgxwCziugwAcj0APQAAREXAfQAycjPhQgS+lKCENf1xWPPC44BERYByz/JgED7AFUC/jHTP9TXTFYZ0NT6SDHUMfpIMdQx0x8x+lAx0dD6SPpQMdEkggDCiALHBfL0AdCUIMcAs4roMNCUIMcAs446INdLAZEwm4E0vAHAAfL010zQ4tM/UhARFoBA9FswyM+PGAAEghDWRsfRzwv3cM8LYRLLP8lw+wARFOgwyM+FCBJWVwCExwWSMX+WUkLHBcMA4vL0ERfQ9AT0BPQEMdFWGALI9AD0APQAycjPhQgT+lKCEBWAAWHPC47LPwERFgH0AMmAQPsAANQg10sBkTCbgTS8AcAB8vTXTNDi0z/6UPpQIm6XUjaAQPRbMJsiyPpSVCBHgED0Q+IhbpdSNYBA9FswmyHI+lJUIEaAQPRD4gPIyz8S+lT6VMnIz48YAASCEJxau5XPC/dxzwthzMlw+wBYAf4g10sBkTCbgTS8AcAB8vTXTNDi0z/TH9Mf+gD6ANMP0w/SAIE6OClWIIBA9A5voTHy9IE6NSLy9IE6NCSBJxC58vSBOjQjgScQufL0gTo1KMIA8vQnyMsfJ88LHyb6AiX6AiTPCw8jzwsPIs8KAFKSER+AQPRDB8jLHxbLH1AEWAAk+lKCECjLzGTPC47LP8mAQPsAAFT6Alj6AssPyw/KAMnIz48YAASCEPvmHxXPC/dwzwthE8s/EszJcPsAERUAZM8L93DPC2Eizws/VhfPFMlw+wDIz4UIFPpSghDhe/PMzwuOEss/yz8BERQBzMmAQPsAAf4g10sBkTCbgTS8AcAB8vTXTNDi0z/U1NSBOjcj0NMHIcFB8oUBqgLXGNHXScMA8vSBOjslVhuAQPQOb6Exs/L0AdDU1NFtAtDSANN303fR+CMiyMt3yz8TygDLd8t3yQHQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kByMzMyfgjXAAuyM+FCBL6UoIQrXgz188Ljss/yYBA+wABtHDIy3fLP3DPC/DJ+CNwyMt3yz9wzwvwyQHIzMzJJAbQlCDHALOK6DAFyMwS9ADME8xSMhEZgED0Q8jPjxgABIIQ7TfEvM8L93DPC2ETyz8BERcBzMlw+wARFV0B/iDXSwGRMJuBNLwBwAHy9NdM0OLTByHBQfKFAaoC1xjIItdJIKk4AvJFqwIgwUHyhc8LBxLOyYE6NyHQ0wchwUHyhQGqAtcY0ddJwwDy9CD5AIE6P1MWgwf0Dm+hMbPy9FRBFoMH9BfIz48YAASCEL8NGrbPC/dwzwthKc8LPxVeAArMyXD7AAL8gTo4JFYYgED0Dm+hEvL01PQE1NTRINDU1NEB0NN30z/SANN303fRIo44XwYB0NTU0QHQ03fTP9IA03fTd9FWItDUMfpIMdQx+kgwUqDwBgTIy3cTyz/KAMt3y3fJyMzMyQHjDQPIzBL0AMzMUkIRGIBA9ENWGdDUMfpIMdQxYWIB9oE6OCRWGIBA9A5voRLy9NT0BNTU0QHQ1NTRAdDTd9M/0gDTd9N30VYi0NQx+kgx1DH6SDBSoPAGBMjLdxPLP8oAy3fLd8nIzMzJA8jMEvQAEszMUkIRGIBA9ENWGdDUMfpIMdQx+kgwBMjLPxT6UgERFvoCycjPjxgABGMAVjZWItDUMfpIMdQx+kgwEEUQNEEwVCag8AYEyMt3E8s/ygDLd8t3ycjMzMkAUPpIMATIyz8U+lIBERb6AsnIz48YAASCEBQffizPC/dxzwthzMlw+wAAJIIQMOur288L93HPC2HMyXD7AAH+BBEWBAMRGwMCERoCAREZAREYVhdWF1YdVh1WHVYd8AwEjihfBFcWVxZXFlcWVxZXFg8RFQ8OERQODRETDQwREgwLERELChEQClVZ4MjPk+n2kRJWG88LPwERIAHMAREeAcsfAREcAfQAVhoB+lTJyAERHPoCAREc+gIBERwBzGUA0AERGgHMycjPk56NQQYBERYByz8BERYB+lIBERcBzAEREwHMycjPhYgBERUB+lJxzwtuAREUAczJgED7AA8RFQ8OERQODRETDQwREgwLERELChEQChCfEI4QfRBsEFsQShA5SBZFVQcDAgEgaGkCAUiEhQIBIGprAgEgfX4CASBsbQIBIHBxAfcJcMAlSdus8MAkXDijidXEgIRFAIBERMBERIjVhVWFVYVVhUr2oMCERQCARETARESgQCFERLegTo+VhvQ1DH6SDHUMfpI10z4KMjPhAL6UhL6UskByM+E0MzM+RbIz4oAQMv/z1AmxwXy9PQEIW6YMSDHAJIwbeCS0dDigbgHzCTDAJUlbrPDAJFw4o4cVxEGERMGBRESBQQREQRVAhEQVhHac0/tgQCFDeAz0ALQAtcsJ9PtIiTyv9M/1NMfMfQB+lAwAdDT/zHU0dDUMdM/+kgx+gAx+kjRBfoAMfoA1NdMB8j6Uhb6UiH6AsnIz48YAASCEDfdb26BvAMogbo4wMG2LBMjPkD4p+pYVyz9QA/oCUhD6UvpU9ADPhCDOycjPhQgS+lJxzwtuzMmAQPsA4Gwi+kjU1NFWGtDUMfpIMdT6SDHUMdMfMfpQMdHQ+kgx+lAx+lAx0QNuk1jwEeFfBAB0zwv3cM8LYRPLPxLMyXD7ACFukl8FjiIDyMwUzMnIz4WIFPpSghD0MqTjzwuOyz8SzAH6AsmAQPsA4gH1CXDAJUubrPDAJFw4o4nVxICERQCARETARESVhRWFFYUVhRWEdpzAhEUAgEREwEREoEAhRES3hEVERoRFREUERkRFBETERgRExESERcREhERERYREREQERoREA8RGQ8OERgODREXDQwRFgwLERoLChEZCgkRGAkIERcIgcgH1CXDAJUtbrPDAJFw4o4oVxIHERQHBhETBgUREgVVAxERLNqHBhETBgUREgUEEREEgQCFERFVMOAi0NP/MdTU9AT0BDHRAtD6SDHTP9Qx0//6SNGBOj1WINDUMfpIMdQx+kgwWMcF8vSBOjkiVh2AQPQOb6Ex8vRWHlYegdAH4BxEWBwYRGgYFERkFBBEYBAMRFwMCERYCAREaAREZVhhWGFYYVh1WHfATA44wXwNXFlcWVxZXFlcWERARFREQDxEUDw4REw4NERINDBERDAsREAsQrxCeEI0QfFVG4BEYER0RGBEXERwRFxEWERsRFhEVERoRFREUERkRFHMAfhETERgRExESERcREhERERYREREQERUREA8RFA8OERMODRESDQwREQwLERALEK8QnhCNEHwQaxBaEEkQOFjwGAH8Vh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYX8A1WHdD0BDH0BPQEMdFSIIBA9A5voZP6SNGSMG3igTo+IW6z8vSBOj5RGscF8vQpwwCWVhRus8MAkXDinlYZVhlWGVYZU8VWGtpg3lYeVh5WHlYedQH8Vh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVh5WHlYeVheBOkARGvAUE/L0Vh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BVh0BERjwFZLwFgASVh4CdgP8Vh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CVh4CgBh/7RHaASDBAJF/liCEd7zDAOKVgTpC8vDgI+MPIlYZyM+Q1H3fjifPCz8mzxQlzwsfUkD6VMkiyMsfIvoCd3h5Av5WHNDUMfpIMdQx+kgx1DHTH/pQMdFSQPAIgTo4IlYbgED0Dm+hEvL01PQE1NTRINDU1NHQ03fTP9IA03fTd9EijjhfBgHQ1NTR0NN30z/SANN303fRViXQ1DH6SDHUMfpIMFKg8AUEyMt3E8s/ygDLd8t3yQHIzMzJAeMNA8jMensB9IE6OCJWG4BA9A5voRLy9NT0BNTU0QHQ1NTR0NN30z/SANN303fRViXQ1DH6SDHUMfpIMFKg8AUEyMt3E8s/ygDLd8t3yQHIzMzJA8jMEvQAEszMUiIRG4BA9ENWHNDUMfpIMdQx+kgwAsjLPxL6UlYZ+gLJyM+PGAAEfADUVhz6AskpViAIViAIViBRgAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAhWIAgHESAHBhEfBgURHgUEER4EAxEeAwIRHwIBESDwFwERFgFQMwBYNlYl0NQx+kgx1DH6SDAQRRA0QTBUJqDwBQTIy3cTyz/KAMt3y3fJAcjMzMkAfBL0AMzMUiIRG4BA9ENWHNDUMfpIMdQx+kgwAsjLPxL6UlYZ+gLJyM+PGAAEghDLxA5bzwv3cc8LYczJcPsAACSCEIuyX6jPC/dxzwthzMlw+wACASB/gAIBIIGCAFEVxJXEF8PMmwzAoBA9A5voZJbcOHUMfQE1DHUMdEB+QABgwf0Dm+hMYABjFcQXw81XwMyIG6RMOAx0IE6QSHXSYMHupch10rAAMMAkXDi8vTT/9GBOkEhhAe78vSAAgRXEF8PNV8DMlMhupJsIeBTIbydAqLwBJWBOkLy8OCpBOACofAElYE6QvLw4IE6QiGZhP8iqQQjvsMAkX/i8vSogAaENzg4ODg4ODk5OTk5PT09PQHDAJUrbrPDAJFw4o4RPBBrEFoQSRA4EEdeI0A02rHgbDMzMzYG0NQx+kgx1DH6SDHUMdMfMfpQ0SBu4wNfB3CCDAIom0NMfMfoA+gAx0fgoB8j6UhPMF8zJyM+RwPCtYhXLPxPMWPoCyx8S+lL0AMnIz4WIEvpSz4QQc/oCcc8LZczJgED7AH8CASCGhwIBIIqLAvMKMMAlS5us8MAkXDijhkLERgLChEXCgkRFgkIERUILtrEAxEQA0/t4DIzA9DT/zHU1DH0BDH0BDHR0PpI0z/UMdP/MfpI0QLI+lLJAsj6Uhf6UlAD+gISzMnIz48YAASCEOnADJfPC/dwzwthFcs/FMzJcPsAIm7jD4IiJAB0MTIgbrOUxwXDAJJbcOKAABF8DADrIAfoCycjPhYgT+lKCEODogvXPC47LP8zJgED7AAH3FcSVxBfDzIzM4E6ODQSgED0Dm+hE/L0AdQx9AQx1NTRAo5XMNDU1NEB0NN30z/SANN303fR+CNQBKEjqBSgI7YI+CMByMt3yz/KABLLd8t3yQHQ03fTP9IA03fTd9H4I1AEoSOoFKAjtgj4IwHIy3fLP8oAEst3y3fJ4IIwArQTXwNXElcQXw8ybDMD0NQx+kgx1DH6SDHUMdMf+lAx0VIw8AgBgED0Dm+hlltwVHAAcOHTH9Mf+gD6ANMP0w/SANGXXwdwVHAAcOEGlDAxA3/gMTQDf4ACuMdDU1NEB0NN30z/SANN303fR+CNQBKEjqBSgI7YI+CMByMt3yz/KABLLd8t3yQHQ03fTP9IA03fTd9H4I1AEoSOoFKAjtgj4IwHIy3fLP8oAEst3y3fJAgEgj5ACASCtrgIBIJGSAgEgn6ACASCTlAIBIJ2eAgEglZYCASCbnAIBZpeYAgFImZoAV6BftRNDU1DHTPzHRINAx1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kgx1DHXCx+AG+hx7UTQ1NQx0z8x0SDQMdQx1DHTBzH0BPQEMdFtIYBA9IZvpTKRAZ1SAm8CURKAQPR8b6Uy6DAxgBjpXXaiaGpqGOmfmOjoahjqaYOY+gIY+gIY6Oh6Ahj6AnoCGOjAIHoHN9DJ/SRoyRg28UASacj2omhqahjpn5jo6GpqGOmDmPoCGPoCGOjoahj9JBjqGP0kGEAaayvxoUNjS3NZcxtDC0txc6N7cXMbG0uBcmN7G1qTK2MrC5sqo3tbK3KDe3tkEWpgXGJcYRAAKmt2HaiaGpqaZ/okWhqammD+gJ6AmjAgEK2tra2trbJeASABgiJBgWIiIWFCIgFCE+IRwhOrxwIPQg0iCwII4gbIqA2qCG2tqghtraoIcCAQygZ+AdAADWzajtRNDU1DHTPzHR0NQx1DHTB/QEMfQEMdGAAobAfO1E0NTU0z/RItDU1NMH9AT0BNGBAIVtbW1tbW2S8AkADBERDAsREAsQrxCeEI0QrBCbEIoQeRBoEFcQRhA1RDBtWm1tWm1tWoEAhlnwFIAIBIKGiAgEgqaoCASCjpABfsnG7UTQ1NQx0z8x0SDQMdTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpIMdQx0x8x+lDRgAgEgpaYCAWKnqACqqoftRNDU1NM/0SLQ1NTTB/QE9ATRgQCFbW1tbW1tkvAJAAwRFQwLERQLChETCgkREgkIEREIDxEQDxDvEN4QfRBsVVVtbVB2bQZtbVB2gQCGVVDwGwBoqR3tRNDU1DHTPzHR0NTUMdMHMfQEMfQEMdHQ1PpIMdQx+kgx1DHTHzH6UDHR0PpI+lAx0QBRofu1E0NTUMdM/MdEg0DHU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SDDHBYAUaB/tRNDU1DHTPzHRINAx1NQx0wcx9AQx9AQx0dDUMddM0PpI+lD6UNGAKGyXrtRNDU1NM/0SLQ1NTTB/QE9ATRgQCFbW1tbW1tkvAJAAwREQwLERALEK8QnhCNEKwQmxCKEHkQaBBXEEYQNUQwbVptbVptbVqBAIZZ8BqACAnWrrABno6+1E0NTUMdM/MdHQ1NQx0wcx9AQx9AQx0dDU+kgx1DH6SDHUMdMfMfpQMdHQ+kgx+lDRgBjohu1E0NTUMdM/MdHQ1DHU0wcx9AQx9AQx0dD0BPQEMfQEMdGAQPQOb6GT+kjRkjBt4oCASCvsAIBILGyAFu0fb2omhqahjpn5jo6GpqGOmDmPoCGPoCGOjoahj9JGoY/SQY6hjpj5j9KBjowAIO33P2omhqahjpn5jokGgY6hjqGOmDmPoCGPoCaMAgegc30McJaY/pj/0AfQBph+mH6QBowIBEzJg2tra2tra2uHFAAn7SNvaiaGpqGOmfmOiQaBjqGOoY6YOY+gJ6AhjowJ0cLMAgegc30Il5emoY+gJqGOoY6LaQwYP6QzfSyE8A6misN4EoiUGD+j430vQIEa+BwAgEgs7QCAWq1tgBDshx7UTQ1NQx0z8x0dDUMdQx0wcx9AT0BDHRgED0Dm+hMYABjpV/aiaGpqGOmfmOiQaBjqGOoY6YOY+gJ6AhjowJ0cLMAgegc30Il5emp6AhjqGOoY6MAT6cD2omhqahjpn5jo6GoY6mmDmPoCGPoCGOjoegIY+gIY+gJogPgB2c='); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4611,6 +4611,7 @@ export class LockReleaseTokenPool implements c.Contract { 'TokenPool_Error.InvalidRequestedFinality': 14921, 'Error.UnexpectedReleaseBounce': 26300, 'RateLimiter_Error.BucketOverfilled': 26300, + 'Error.ContextExecutorUnavailable': 26301, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, 'RateLimiter_Error.TokenRateLimitReached': 26302, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, From 57e1f96b193cadf3ee3d218a71cfe0b708621105 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Fri, 31 Jul 2026 10:13:11 +0200 Subject: [PATCH 03/11] Lock or burn transfer error handling and return --- .../ccip/pools/burn_mint/contract.tolk | 14 +- .../ccip/pools/lib/token_pool/entrypoint.tolk | 150 +++++++++++++---- .../ccip/pools/lib/token_pool/errors.tolk | 5 - .../pools/lock_release_lockbox/contract.tolk | 3 +- .../ccip/pools/BurnMintTokenPool.spec.ts | 115 ++++++++----- .../pools/LockReleaseLockboxTokenPool.spec.ts | 53 +++--- .../ccip/pools/LockReleaseTokenPool.spec.ts | 151 ++++++++++++------ .../gen/ccip/pools/BurnMintTokenPool.ts | 4 +- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 4 +- .../gen/ccip/pools/LockReleaseTokenPool.ts | 4 +- 10 files changed, 340 insertions(+), 163 deletions(-) diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 83194b511..6ad573cdb 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -108,8 +108,11 @@ fun onBouncedMessage(in: InMessageBounced) { fun onLockOrBurnTransferContinue( st: Storage, sender: address, + _msg: TransferNotificationForRecipient, fwdPayload: TokenPool_LockOrBurnForwardPayload, ): Storage { + assert(st.canTakeContextExecutorNextId()) throw BurnMintTokenPool_Error.ContextExecutorUnavailable; + val requestMsg = lazy fwdPayload.requestMsg.load(); val jettonClient = st.poolData.load().jettonClient(); val context = BurnMintTokenPool_BurnContext { @@ -204,15 +207,6 @@ fun onReleaseOrMintContinue( return st; } -fun onLockOrBurnTransfer( - st: Storage, - _sender: address, - _msg: TransferNotificationForRecipient, -): Storage { - assert(st.canTakeContextExecutorNextId()) throw BurnMintTokenPool_Error.ContextExecutorUnavailable; - return st; -} - fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { assert(st.canTakeContextExecutorNextId()) throw BurnMintTokenPool_Error.ContextExecutorUnavailable; return st; @@ -407,7 +401,7 @@ fun loadPool(st: Storage): TokenPool { onLockOrBurn: null, validateLockOrBurn: null, preflightCheck: null, - onLockOrBurnTransfer, + onLockOrBurnTransfer: null, onLockOrBurnTransferContinue, onReleaseOrMint, validateReleaseOrMint: null, diff --git a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk index 15182f95b..5b92144d0 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk @@ -1,5 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 import "@stdlib/lisp-lists" +import "@stdlib/gas-payments" import "../../../../lib/math" import "../../../../lib/utils" @@ -32,6 +33,11 @@ import "../rate_limiter" /// 0.000567 tokens. /// In the case of a burnMint pool on chain A, these funds are burned in the pool on chain A. /// In the case of a lockRelease pool on chain A, these funds accumulate in the pool on chain A. +/// TON value needed to return Jettons after a recoverable transfer failure. +/// Any value left from the inbound notification is forwarded with the failure +/// notification after the pool's pre-message balance has been reserved. +const TokenPool_RETURN_TRANSFER_VALUE = ton("0.1"); + struct TokenPool { data: TokenPool_Data; context: T? = null; @@ -89,7 +95,7 @@ struct TokenPool_Hooks { validateLockOrBurn: ((T, address, TokenPool_LockOrBurn) -> (T, bool, TokenPool_LockOrBurnPrepared))?; preflightCheck: ((T?, TokenPool_LockOrBurn, TokenPool_LockOrBurnForwardPayload) -> bool)?; onLockOrBurnTransfer: ((T, address, TransferNotificationForRecipient) -> T)?; - onLockOrBurnTransferContinue: ((T, address, TokenPool_LockOrBurnForwardPayload) -> T)?; + onLockOrBurnTransferContinue: ((T, address, TransferNotificationForRecipient, TokenPool_LockOrBurnForwardPayload) -> T)?; } @inline @@ -564,7 +570,7 @@ fun TokenPool.onInternalMessage(mutate self, msgSender: address, msgValue: co // // In the worst case, not enough funds, received Jettons are burned. TransferNotificationForRecipient => { - self.onLockOrBurnTransfer(msgSender, msg); + self.onLockOrBurnTransfer(msgSender, msg, msgValue); } TokenPool_ReleaseOrMint => { self.onReleaseOrMint(msgSender, msg); @@ -676,7 +682,7 @@ fun TokenPool.onPreflightCheckFailed( dest: requestMsg.replyTo!, body: TokenPool_LockOrBurnFailure { queryId: requestMsg.queryId, - errorCode: TokenPool_errorCode(TokenPool_Error.UnsupportedOperation), + errorCode: TokenPool_Error.UnsupportedOperation as uint16, }, }); failure.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); @@ -727,7 +733,7 @@ fun TokenPool.onPostflightCheckFailed( dest: requestMsg.replyTo!, body: TokenPool_ReleaseOrMintFailure { queryId: requestMsg.queryId, - errorCode: TokenPool_errorCode(TokenPool_Error.UnsupportedOperation), + errorCode: TokenPool_Error.UnsupportedOperation as uint16, }, }); failure.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); @@ -983,14 +989,45 @@ fun TokenPool.onLockOrBurnTransfer( mutate self, sender: address, msg: TransferNotificationForRecipient, + msgValue: coins, ): void { if (self.hooks != null && self.hooks.onLockOrBurnTransfer != null) { self.context = self.hooks.onLockOrBurnTransfer(self.context!, sender, msg); + + return; } - // Check the notification originates from a valid Jetton wallet + // This is a trust boundary, rather than a recoverable transfer failure. + // Never return tokens in response to a notification from an arbitrary + // contract: only this pool's Jetton wallet can have credited custody. assert(self.data.jettonClient().isWallet(sender), TokenPool_Error.Unauthorized); + try { + val forwardPayload = loadForwardPayloadAsSlice(msg.forwardPayload); + val fwdp = TokenPool_LockOrBurnForwardPayload.fromSlice(forwardPayload!); + + // A valid pool wallet has already credited the tokens before it + // delivers this notification. Once its payload is decoded, every + // failure can return custody and notify the original requester. + try { + self.processLockOrBurnTransfer(sender, msg, fwdp); + } catch (errorCode) { + self.failLockOrBurnTransfer(sender, msg, fwdp, errorCode as uint16, msgValue); + } + } catch { + // A malformed payload has no reliable reply target, but custody can + // still be returned to a valid pool wallet. + self.bestEffortReturnTransfer(sender, msg); + } +} + +/// Process a decoded Jetton transfer notification after its sender has been authenticated. +fun TokenPool.processLockOrBurnTransfer( + mutate self, + sender: address, + msg: TransferNotificationForRecipient, + fwdp: TokenPool_LockOrBurnForwardPayload, +): void { // We either get this transfer from the Router, in which case we trust the transfer is valid; // Or for a different sender (e.g., per-user deposit account), we need to validate the transfer, // which we can only do with context -> recover from ContextExecutor. @@ -998,52 +1035,93 @@ fun TokenPool.onLockOrBurnTransfer( // The context contains info about the sender, the amount, etc. that we can use to validate the transfer. // If we can't validate the transfer, we try to return the funds. - // TODO: check message value is enough to cover full flow, or error - val fwdps = loadForwardPayloadAsSlice(msg.forwardPayload); - if (fwdps == null) { - self.returnTransfer(sender, msg); - return; - } - val fwdp = TokenPool_LockOrBurnForwardPayload.fromSlice(fwdps!); - - // TODO: can we trust (any) JettonWallet forward payloads? - // assert(request.amount == msg.jettonAmount, TokenPool_Error.AmountMismatch); - - // Continue if transfer initiator is the Router, otherwise try to validate the transfer - val router = self.data.adminConfig.load().dynamicConfig.load().router; - // TODO: should check if transferInitiator == walletAddress(router) - missing info for now - // if (msg.transferInitiator != null && msg.transferInitiator! == router) { - if (msg.transferInitiator != null) { - self.onLockOrBurnTransferContinue(sender, fwdp); - return; - } + val requestMsg = lazy fwdp.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); - // TODO: should throw as unsupported for now - // TODO: check if this is a registered deposit account - // ContextExecutor_Ask(fwdp: msg + fwdp) -> ContextExecutor_Reply(ctx, fwdp) - // if reply is valid, continue, else return transfer + assert(details.amount == msg.jettonAmount, TokenPool_Error.AmountMismatch); + assert(msg.transferInitiator != null, TokenPool_Error.MissingTransferInitiator); + self.onLockOrBurnTransferContinue(sender, msg, fwdp); } fun TokenPool.onLockOrBurnTransferContinue( mutate self, sender: address, + msg: TransferNotificationForRecipient, fwdPayload: TokenPool_LockOrBurnForwardPayload, ): void { if (self.hooks != null && self.hooks.onLockOrBurnTransferContinue != null) { - self.context = self.hooks.onLockOrBurnTransferContinue(self.context!, sender, fwdPayload); - + self.context = self.hooks.onLockOrBurnTransferContinue(self.context!, sender, msg, fwdPayload); return; } return self.onLockOrBurnTransferFinalize(fwdPayload); } -fun TokenPool.returnTransfer(self, sender: address, msg: TransferNotificationForRecipient): void { - // TODO: check available gas, we don't want to drain the contract with return transfers - // send back the tokens, we don't support direct transfers to the pool +/// Returns a failure notification to the original requester, carrying the remaining value of the inbound transfer. +/// The pool's balance is reserved to ensure that the notification can be sent safely (without consuming the pool's balance). +fun TokenPool.notifyLockOrBurnFailure( + self, + msgValue: coins, + forwardPayload: TokenPool_LockOrBurnForwardPayload, + errorCode: uint16, +): void { + self.reserveLockOrBurnFailureValue(msgValue); + + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val failure = createMessage({ + bounce: false, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_LockOrBurnFailure { + queryId: requestMsg.queryId, + errorCode, + }, + }); + failure.send(SEND_MODE_CARRY_ALL_BALANCE | SEND_MODE_IGNORE_ERRORS); + } +} + +/// Keep the pool's balance from before the inbound Jetton notification, then +/// allow recovery messages to carry only the notification's remaining value. +fun TokenPool.reserveLockOrBurnFailureValue(self, msgValue: coins): void { + // Storage accounting can leave a minimally funded pool with less balance + // than the forwarded value. Saturate rather than throwing before recovery. + val originalBalance = contract.getOriginalBalance(); + val balanceBeforeMessage = originalBalance > msgValue + ? originalBalance - msgValue + : 0 as coins; + val toLeaveOnBalance = balanceBeforeMessage + contract.getStorageDuePayment(); + reserveToncoinsOnBalance(toLeaveOnBalance, RESERVE_MODE_AT_MOST); +} + +/// Returns custody to the sender and notifies the original requester of a failed transfer. +fun TokenPool.failLockOrBurnTransfer( + self, + sender: address, + msg: TransferNotificationForRecipient, + forwardPayload: TokenPool_LockOrBurnForwardPayload, + errorCode: uint16, + msgValue: coins, +): void { + self.bestEffortReturnTransfer(sender, msg); + self.notifyLockOrBurnFailure(msgValue, forwardPayload, errorCode); +} + +fun TokenPool.bestEffortReturnTransfer(self, sender: address, msg: TransferNotificationForRecipient): void { + // Keep the helper safe if a future caller uses it directly. The outer + // entrypoint performs the same assertion before any recoverable catch. + assert(self.data.jettonClient().isWallet(sender), TokenPool_Error.Unauthorized); + + if (msg.transferInitiator == null) { + return; // nothing to do, no one to return to + } + + // Send back tokens; direct transfers to the pool are not supported. val returnTransfer = createMessage({ bounce: BounceMode.NoBounce, - value: 0, + value: TokenPool_RETURN_TRANSFER_VALUE, dest: sender, // the wallet we own which sent us the notification body: AskToTransfer { queryId: msg.queryId, @@ -1055,7 +1133,9 @@ fun TokenPool.returnTransfer(self, sender: address, msg: TransferNotification forwardPayload: beginCell().storeBool(false).endCell().beginParse(), }, }); - returnTransfer.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + // Returning custody is best-effort. The remaining inbound value is carried + // by the subsequent failure notification, after reserving pool balance. + returnTransfer.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_IGNORE_ERRORS); } fun TokenPool.onLockOrBurnTransferFinalize( diff --git a/contracts/contracts/ccip/pools/lib/token_pool/errors.tolk b/contracts/contracts/ccip/pools/lib/token_pool/errors.tolk index 93324e967..598337bff 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/errors.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/errors.tolk @@ -31,8 +31,3 @@ enum TokenPool_Error { InvalidRequestedFinality RateLimitExceeded } - -@inline -fun TokenPool_errorCode(local: uint16): uint16 { - return getErrorCode("link.chain.ton.ccip.TokenPool".crc32(), local); -} diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index f0b93dff3..ab5da4b7c 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -91,12 +91,13 @@ fun onBouncedMessage(in: InMessageBounced) { fun onLockOrBurnTransferContinue( st: Storage, sender: address, + _msg: TransferNotificationForRecipient, fwdPayload: TokenPool_LockOrBurnForwardPayload, ): Storage { val requestMsg = lazy fwdPayload.requestMsg.load(); val prepared = lazy fwdPayload.prepared.load(); - assert(!st.pendingLocks.get(requestMsg.queryId).isFound, LockReleaseLockBoxTokenPool_Error.PendingLockAlreadyExists); + assert(!st.pendingLocks.get(requestMsg.queryId).isFound) throw LockReleaseLockBoxTokenPool_Error.PendingLockAlreadyExists; // Store pending lock data for finalization. st.pendingLocks.set(requestMsg.queryId, LockReleaseLockboxTokenPool_PendingLock { diff --git a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts index 69e8c5bdd..75aeb2630 100644 --- a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts @@ -16,6 +16,7 @@ import { TokenPool_RateLimitConfigPair, TokenPool_ChainUpdate, TokenPool_LockOrBurn, + TokenPool_LockOrBurnFailure, TokenPool_LockOrBurnInV1, TokenPool_LockOrBurnFinished, TokenPool_LockOrBurnForwardPayload, @@ -342,7 +343,7 @@ describe('BurnMintTokenPool', () => { }) }) - it('reverts lockOrBurn when caller is not configured on-ramp', async () => { + it('returns transfer inputs that bypass the prevalidated forward path', async () => { const unauthorizedWallet = await userWallet(unauthorized.address) const poolWallet = await userWallet(burnMintPool.address) const result = await unauthorizedWallet.sendTransfer(unauthorized.getSender(), { @@ -380,14 +381,46 @@ describe('BurnMintTokenPool', () => { expect(result.transactions).toHaveTransaction({ from: poolWallet.address, to: burnMintPool.address, - success: false, + success: true, }) + expect(await unauthorizedWallet.getJettonBalance()).toEqual(toNano('2')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) }) - describe('lockOrBurn transfer input validation (current behavior)', () => { - it('currently aborts when payload amount does not match transferred amount', async () => { + describe('lockOrBurn transfer input validation', () => { + it('returns tokens and notifies the requester when payload amount does not match transferred amount', async () => { const onRampWallet = await userWallet(deployer.address) const poolWallet = await userWallet(burnMintPool.address) + const requestMsg = TokenPool_LockOrBurn.create({ + queryId: 304n, + request: TokenPool_LockOrBurnInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 304n, + details: TokenPool_TransferDetails.create({ + receiver: receiverAddress, + remoteChainSelector, + originalSender: deployer.address, + amount: toNano('1'), + localToken: cctMinter.address, + }), + }), + }), + requestedFinalityConfig: 0n, + tokenArgs: null, + replyTo: deployer.address, + }) + const forwardPayload = TokenPool_LockOrBurnForwardPayload.create({ + originalSender: deployer.address, + requestMsg, + prepared: TokenPool_LockOrBurnPrepared.create({ + feeAmount: 0n, + destTokenAmount: toNano('1'), + out: TokenPool_LockOrBurnOutV1.create({ + destTokenAddress, + destPoolData: Cell.EMPTY, + }), + }), + }) const result = await onRampWallet.sendTransfer(deployer.getSender(), { value: toNano('2'), message: { @@ -397,51 +430,40 @@ describe('BurnMintTokenPool', () => { responseDestination: deployer.address, customPayload: null, forwardTonAmount: toNano('0.2'), - forwardPayload: TokenPool_LockOrBurn.toCell( - TokenPool_LockOrBurn.create({ - queryId: 304n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 304n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('1'), - localToken: cctMinter.address, - }), - }), - }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }), - ), + forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), }, }) expect(result.transactions).toHaveTransaction({ from: poolWallet.address, to: burnMintPool.address, - success: false, + success: true, }) - }) - - it('currently aborts when transfer forward payload is malformed', async () => { - await cctMinterRuntime.sendMint(deployer.getSender(), { - value: toNano('1'), - mintOpcode: 0x00000015, - message: { - queryId: 101n, - destination: deployer.address, - tonAmount: toNano('0.05'), - jettonAmount: toNano('10'), - from: deployer.address, - responseDestination: deployer.address, - forwardTonAmount: 0n, + expect(result.transactions).toHaveTransaction({ + from: burnMintPool.address, + to: poolWallet.address, + success: true, + op: 0x0f8a7ea5, // AskToTransfer + }) + expect(result.transactions).toHaveTransaction({ + from: burnMintPool.address, + to: deployer.address, + // The Treasury test recipient does not implement this callback. Assert + // the emitted message, including that it carries the residual inbound + // value instead of consuming it in the pool. + op: TokenPool_LockOrBurnFailure.PREFIX, + value: (value) => value !== undefined && value > 0n, + body(body) { + if (!body) return false + const failure = TokenPool_LockOrBurnFailure.fromSlice(body.beginParse()) + return failure.queryId === 304n && failure.errorCode === 14920n }, }) + expect(await onRampWallet.getJettonBalance()).toEqual(toNano('10')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) + }) + it('returns tokens without a callback when transfer forward payload is malformed', async () => { const deployerWallet = await userWallet(deployer.address) const poolWallet = await userWallet(burnMintPool.address) @@ -461,8 +483,21 @@ describe('BurnMintTokenPool', () => { expect(result.transactions).toHaveTransaction({ from: poolWallet.address, to: burnMintPool.address, - success: false, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: burnMintPool.address, + to: poolWallet.address, + success: true, + op: 0x0f8a7ea5, // AskToTransfer + }) + expect(result.transactions).not.toHaveTransaction({ + from: burnMintPool.address, + to: deployer.address, + op: TokenPool_LockOrBurnFailure.PREFIX, }) + expect(await deployerWallet.getJettonBalance()).toEqual(toNano('10')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) }) }) diff --git a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts index 6a6721f39..e1acbf943 100644 --- a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts @@ -14,6 +14,7 @@ import { TokenPool_MirroredPolicy, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, + TokenPool_LockOrBurnFailure, TokenPool_LockOrBurnForwardPayload, TokenPool_LockOrBurnPrepared, TokenPool_LockOrBurnOutV1, @@ -755,23 +756,9 @@ describe('LockReleaseLockboxTokenPool', () => { // clearing the pending lock and emitting TokenPool_LockedOrBurned. }) - it('should return jettons for transfers without transferInitiator (direct user transfers)', async () => { - // When a user sends jettons directly to the pool wallet (not through on-ramp), - // the transferInitiator will be null (user's own wallet). - // The pool should detect this and return the jettons instead of processing. - // This is tested by verifying that onLockOrBurnTransfer checks transferInitiator != null. - // The TokenPool library handles this - if transferInitiator is null, it calls returnTransfer(). - // In the current test setup, all transfers go through JettonWallet which sets transferInitiator. - // The onLockOrBurnTransfer hook in token_pool.tolk asserts: - // if (msg.transferInitiator != null) { onLockOrBurnTransferContinue(); return; } - // else { returnTransfer(); } - // This prevents rogue transfers from being processed as locks. - }) - - it('should reject duplicate lock requests with PendingLockAlreadyExists', async () => { - // This tests the onLockOrBurnTransferContinue hook directly. - // When a lock request with the same queryId arrives twice, the second should be rejected. - // The contract asserts: !pendingLocks.get(msg.queryId).isFound -> PendingLockAlreadyExists + it('returns duplicate lock transfers and notifies the requester', async () => { + // The pending lock from the first request makes the second continuation fail. + // The shared TP path must recover those already-received jettons. const onRampWallet = await userWallet(deployer.address) const poolWallet = await userWallet(lockReleaseLockboxPool.address) @@ -828,10 +815,7 @@ describe('LockReleaseLockboxTokenPool', () => { }) expect(await lockReleaseLockboxPool.getHasPendingLock(310n)).toBe(true) - // Second lock request with same queryId - should be rejected by onLockOrBurnTransferContinue - // The pool's jetton wallet will forward this back to the pool - // The pool processes it in onLockOrBurnTransfer which checks transferInitiator - // Then calls onLockOrBurnTransferContinue which checks for duplicate queryId + // Second lock request with the same queryId is returned by the shared continuation path. const secondResult = await onRampWallet.sendTransfer(deployer.getSender(), { value: toNano('3'), message: { @@ -845,15 +829,36 @@ describe('LockReleaseLockboxTokenPool', () => { }, }) - // The pool rejects duplicate with exit code 48700 (PendingLockAlreadyExists) expect(secondResult.transactions).toHaveTransaction({ + from: poolWallet.address, to: lockReleaseLockboxPool.address, - success: false, - exitCode: 48700, + success: true, + }) + expect(secondResult.transactions).toHaveTransaction({ + from: lockReleaseLockboxPool.address, + to: poolWallet.address, + success: true, + op: 0x0f8a7ea5, // AskToTransfer + }) + expect(secondResult.transactions).toHaveTransaction({ + from: lockReleaseLockboxPool.address, + to: deployer.address, + // The Treasury test recipient does not implement this callback. Assert + // the emitted message, including that it carries the residual inbound + // value instead of consuming it in the pool. + op: TokenPool_LockOrBurnFailure.PREFIX, + value: (value) => value !== undefined && value > 0n, + body(body) { + if (!body) return false + const failure = TokenPool_LockOrBurnFailure.fromSlice(body.beginParse()) + return failure.queryId === 310n && failure.errorCode === 48700n + }, }) // Original pending lock should still exist expect(await lockReleaseLockboxPool.getHasPendingLock(310n)).toBe(true) + expect(await onRampWallet.getJettonBalance()).toEqual(toNano('995')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) }) }) diff --git a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts index dcfd14ede..c64b2c539 100644 --- a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts @@ -15,7 +15,11 @@ import { TokenPool_ReleaseOrMintFailure, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, + TokenPool_LockOrBurnFailure, + TokenPool_LockOrBurnForwardPayload, TokenPool_LockOrBurnInV1, + TokenPool_LockOrBurnOutV1, + TokenPool_LockOrBurnPrepared, TokenPool_ReleaseOrMintInV1, TokenPool_RateLimitConfigPair, TokenPool_RampUpdate, @@ -311,10 +315,40 @@ describe('LockReleaseTokenPool', () => { } }) - describe('lockOrBurn transfer input validation (current behavior)', () => { - it('currently aborts when forwarded amount does not match transfer amount', async () => { + describe('lockOrBurn transfer input validation', () => { + it('returns tokens and notifies the requester when forwarded amount does not match transfer amount', async () => { const onRampWallet = await userWallet(jettonSender.address) const poolWallet = await userWallet(lockReleasePool.address) + const requestMsg = TokenPool_LockOrBurn.create({ + queryId: 44n, + request: TokenPool_LockOrBurnInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 44n, + details: TokenPool_TransferDetails.create({ + receiver: receiverAddress, + remoteChainSelector, + originalSender: deployer.address, + amount: toNano('2'), + localToken: jettonMinter.address, + }), + }), + }), + requestedFinalityConfig: 0n, + tokenArgs: null, + replyTo: deployer.address, + }) + const forwardPayload = TokenPool_LockOrBurnForwardPayload.create({ + originalSender: deployer.address, + requestMsg, + prepared: TokenPool_LockOrBurnPrepared.create({ + feeAmount: 0n, + destTokenAmount: toNano('2'), + out: TokenPool_LockOrBurnOutV1.create({ + destTokenAddress, + destPoolData: Cell.EMPTY, + }), + }), + }) const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { value: toNano('2'), @@ -324,37 +358,40 @@ describe('LockReleaseTokenPool', () => { destination: lockReleasePool.address, customPayload: beginCell().storeBit(1).endCell(), forwardTonAmount: toNano('0.2'), - forwardPayload: TokenPool_LockOrBurn.toCell( - TokenPool_LockOrBurn.create({ - queryId: 44n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 44n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('2'), - localToken: jettonMinter.address, - }), - }), - }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }), - ), + forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), }, }) expect(result.transactions).toHaveTransaction({ from: poolWallet.address, to: lockReleasePool.address, - success: false, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: lockReleasePool.address, + to: poolWallet.address, + success: true, + op: 0x0f8a7ea5, // AskToTransfer }) + expect(result.transactions).toHaveTransaction({ + from: lockReleasePool.address, + to: deployer.address, + // The Treasury test recipient does not implement this callback. Assert + // the emitted message, including that it carries the residual inbound + // value instead of consuming it in the pool. + op: TokenPool_LockOrBurnFailure.PREFIX, + value: (value) => value !== undefined && value > 0n, + body(body) { + if (!body) return false + const failure = TokenPool_LockOrBurnFailure.fromSlice(body.beginParse()) + return failure.queryId === 44n && failure.errorCode === 14920n + }, + }) + expect(await onRampWallet.getJettonBalance()).toEqual(toNano('10')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) }) - it('currently aborts when forward payload is malformed', async () => { + it('returns tokens without a callback when forward payload is malformed', async () => { const onRampWallet = await userWallet(jettonSender.address) const poolWallet = await userWallet(lockReleasePool.address) @@ -373,8 +410,21 @@ describe('LockReleaseTokenPool', () => { expect(result.transactions).toHaveTransaction({ from: poolWallet.address, to: lockReleasePool.address, - success: false, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: lockReleasePool.address, + to: poolWallet.address, + success: true, + op: 0x0f8a7ea5, // AskToTransfer }) + expect(result.transactions).not.toHaveTransaction({ + from: lockReleasePool.address, + to: deployer.address, + op: TokenPool_LockOrBurnFailure.PREFIX, + }) + expect(await onRampWallet.getJettonBalance()).toEqual(toNano('10')) + expect(await poolWallet.getJettonBalance()).toEqual(0n) }) }) @@ -471,6 +521,36 @@ describe('LockReleaseTokenPool', () => { it('locks tokens through a jetton transfer notification and credits the pool wallet', async () => { const onRampWallet = await userWallet(jettonSender.address) const poolWallet = await userWallet(lockReleasePool.address) + const lockOrBurn = TokenPool_LockOrBurn.create({ + queryId: 11n, + request: TokenPool_LockOrBurnInV1.create({ + transfer: TokenPool_Transfer.create({ + id: 11n, + details: TokenPool_TransferDetails.create({ + receiver: receiverAddress, + remoteChainSelector, + originalSender: deployer.address, + amount: toNano('3'), + localToken: jettonMinter.address, + }), + }), + }), + requestedFinalityConfig: 0n, + tokenArgs: null, + replyTo: deployer.address, + }) + const forwardPayload = TokenPool_LockOrBurnForwardPayload.create({ + originalSender: deployer.address, + requestMsg: lockOrBurn, + prepared: TokenPool_LockOrBurnPrepared.create({ + feeAmount: 0n, + destTokenAmount: toNano('3'), + out: TokenPool_LockOrBurnOutV1.create({ + destTokenAddress, + destPoolData: Cell.EMPTY, + }), + }), + }) const result = await jettonSender.sendJettonsExtended(deployer.getSender(), { value: toNano('2'), @@ -480,26 +560,7 @@ describe('LockReleaseTokenPool', () => { destination: lockReleasePool.address, customPayload: beginCell().storeBit(1).endCell(), forwardTonAmount: toNano('0.2'), - forwardPayload: TokenPool_LockOrBurn.toCell( - TokenPool_LockOrBurn.create({ - queryId: 11n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 11n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('3'), - localToken: jettonMinter.address, - }), - }), - }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }), - ), + forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), }, }) diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index 4cdf9489b..b26ffd49e 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -4872,7 +4872,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4891,6 +4891,8 @@ export class BurnMintTokenPool implements c.Contract { 'TokenPool_Error.InvalidRemoteChainDecimals': 14913, 'TokenPool_Error.OverflowDetected': 14914, 'TokenPool_Error.UnsupportedOperation': 14917, + 'TokenPool_Error.MissingTransferInitiator': 14919, + 'TokenPool_Error.AmountMismatch': 14920, 'TokenPool_Error.InvalidRequestedFinality': 14921, 'RateLimiter_Error.BucketOverfilled': 26300, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 481b4d28b..3af8d936f 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -4719,7 +4719,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4738,6 +4738,8 @@ export class LockReleaseLockboxTokenPool implements c.Contract { 'TokenPool_Error.InvalidRemoteChainDecimals': 14913, 'TokenPool_Error.OverflowDetected': 14914, 'TokenPool_Error.UnsupportedOperation': 14917, + 'TokenPool_Error.MissingTransferInitiator': 14919, + 'TokenPool_Error.AmountMismatch': 14920, 'TokenPool_Error.InvalidRequestedFinality': 14921, 'RateLimiter_Error.BucketOverfilled': 26300, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 99a1d019b..5c30c4c51 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -4589,7 +4589,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4608,6 +4608,8 @@ export class LockReleaseTokenPool implements c.Contract { 'TokenPool_Error.InvalidRemoteChainDecimals': 14913, 'TokenPool_Error.OverflowDetected': 14914, 'TokenPool_Error.UnsupportedOperation': 14917, + 'TokenPool_Error.MissingTransferInitiator': 14919, + 'TokenPool_Error.AmountMismatch': 14920, 'TokenPool_Error.InvalidRequestedFinality': 14921, 'Error.UnexpectedReleaseBounce': 26300, 'RateLimiter_Error.BucketOverfilled': 26300, From 6609721fc6dabd583f95f660b2813becbbb62e45 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Fri, 31 Jul 2026 10:20:03 +0200 Subject: [PATCH 04/11] LockReleaseLockboxTokenPool use ContextExecutor vs. local st --- .../pools/lock_release_lockbox/contract.tolk | 330 ++++++++------- .../pools/lock_release_lockbox/errors.tolk | 5 +- .../pools/lock_release_lockbox/messages.tolk | 9 +- .../pools/lock_release_lockbox/storage.tolk | 22 +- .../pools/lock_release_lockbox/types.tolk | 17 +- .../ccip/pools/lockbox/JettonLockBox.tolk | 6 +- .../ccip/pools/lockbox/messages.tolk | 4 + .../tests/ccip/pools/JettonLockbox.spec.ts | 4 + .../pools/LockReleaseLockboxTokenPool.spec.ts | 232 +---------- .../wrappers/gen/ccip/pools/JettonLockBox.ts | 30 +- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 381 ++++++++++++------ 11 files changed, 516 insertions(+), 524 deletions(-) diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index ab5da4b7c..faa891f85 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -8,6 +8,8 @@ import "../../../lib/jetton/messages" import "../../../lib/jetton/jetton_client" import "../../../lib/jetton/jetton-utils" import "../../common/types" +import "../../executors/context_executor/messages" +import "../../executors/context_executor/types" import "../../rmn_remote/lib" import "../lib/token_pool/entrypoint" import "../lib/token_pool/types" @@ -47,18 +49,19 @@ fun onInternalMessage(in: InMessage): void { val msg = lazy LockReleaseLockboxTokenPool_InMessage.fromSlice(in.body); match (msg) { JettonLockBox_Deposited => { - // Lock flow: lockbox confirmed deposit → restore context → finalize onDeposited(mutate st, in.senderAddress, msg); st.store(); } JettonLockBox_WithdrawFailed => { - // Release flow: lockbox reported withdraw failure → restore context → send failure onWithdrawFailed(mutate st, in.senderAddress, msg); st.store(); } ReturnExcessesBack => { - // Both flows: jetton excesses returned → check which flow this is - onReturnExcessesBack(mutate st, in.senderAddress, msg); + onReturnExcessesBack(st, in.senderAddress, msg); + st.store(); + } + ContextExecutor_ForwardNotification => { + onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); } else => { @@ -67,6 +70,16 @@ fun onInternalMessage(in: InMessage): void { } } +fun deployLockReleaseLockboxContextExecutor(executor: AutoDeployAddress): void { + val deploy = createMessage({ + bounce: BounceMode.NoBounce, + value: LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, + dest: executor, + body: beginCell().endCell().beginParse(), + }); + deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + fun onBouncedMessage(in: InMessageBounced) { val msg = lazy LockReleaseLockboxTokenPool_BouncedMessage.fromSlice(in.bouncedBody.skipBouncedPrefix()); match (msg) { @@ -84,35 +97,26 @@ fun onBouncedMessage(in: InMessageBounced) { * 1. Router sends TokenPool_LockOrBurn + jettons via TransferNotificationForRecipient * 2. TokenPool library processes validation, calls onLockOrBurnTransferContinue * 3. We transfer jettons from our wallet to the lockbox - * 4. Lockbox receives TransferNotificationForRecipient, sends JettonLockBox_Deposited back - * 5. We finalize: emit event + send reply + * 4. Lockbox confirms the deposit directly to the pool + * 5. The confirmation carries the lock context and we finalize */ fun onLockOrBurnTransferContinue( st: Storage, - sender: address, + _sender: address, _msg: TransferNotificationForRecipient, fwdPayload: TokenPool_LockOrBurnForwardPayload, ): Storage { val requestMsg = lazy fwdPayload.requestMsg.load(); val prepared = lazy fwdPayload.prepared.load(); - assert(!st.pendingLocks.get(requestMsg.queryId).isFound) throw LockReleaseLockBoxTokenPool_Error.PendingLockAlreadyExists; - - // Store pending lock data for finalization. - st.pendingLocks.set(requestMsg.queryId, LockReleaseLockboxTokenPool_PendingLock { - queryId: requestMsg.queryId, + val context = LockReleaseLockboxTokenPool_LockContext { forwardPayload: fwdPayload.toCell(), - }.toCell()); + }; // Calculate addresses. val jettonClient = st.poolData.load().jettonClient(); val ownWallet = jettonClient.walletAddress(); - val lockboxWallet = calcAddressOfJettonWallet( - st.lockbox, - jettonClient.masterAddress, - jettonClient.jettonWalletCode, - ); // Build JettonLockBox_Deposit using generated struct (avoids manual encoding issues). // Navigate: requestMsg (TokenPool_LockOrBurn) -> request (TokenPool_LockOrBurnInV1) -> transfer -> details. @@ -123,6 +127,7 @@ fun onLockOrBurnTransferContinue( token: jettonClient.masterAddress, remoteChainSelector: requestDetails.remoteChainSelector, amount: prepared.destTokenAmount, + context: context.toCell(), }; // Wrap deposit message in forwardPayload cell (flag + ref, same pattern as lockbox uses). @@ -134,6 +139,8 @@ fun onLockOrBurnTransferContinue( .beginParse(); // Transfer jettons from our wallet to the lockbox wallet. + // The CCT JettonWallet treats transferRecipient as an owner address and + // computes the wallet from it, so we pass the lockbox owner (not wallet) address. val transfer = createMessage({ bounce: BounceMode.RichBounce, value: ton("0.1"), @@ -141,9 +148,9 @@ fun onLockOrBurnTransferContinue( body: AskToTransfer { queryId: requestMsg.queryId, jettonAmount: prepared.destTokenAmount, - transferRecipient: lockboxWallet, + transferRecipient: st.lockbox, sendExcessesTo: contract.getAddress(), - customPayload: null, + customPayload: context.toCell(), forwardTonAmount: 0, forwardPayload: forwardPayload, }, @@ -153,54 +160,31 @@ fun onLockOrBurnTransferContinue( return st; } -/* --- JettonLockBox_Deposited callback --- */ +/* --- Bounced AskToTransfer during lock --- */ fun onDeposited( mutate st: Storage, sender: address, msg: JettonLockBox_Deposited, ) { - // Validate sender is our lockbox. assert(sender == st.lockbox, LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender); - - val pendingEntry = st.pendingLocks.get(msg.queryId); - assert(pendingEntry.isFound, LockReleaseLockBoxTokenPool_Error.PendingLockNotFound); - - val pending = pendingEntry.loadValue().load(); - st.pendingLocks.delete(msg.queryId); - - // Finalize lock flow. + assert(msg.context != null, LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender); + val context = lazy LockReleaseLockboxTokenPool_LockContext.fromCell(msg.context!); + assert(msg.queryId == context.forwardPayload.load().requestMsg.load().queryId) throw 0xFFFF; var pool = loadPool(st); - pool.onLockOrBurnTransferFinalize(pending.forwardPayload.load()); + pool.onLockOrBurnTransferFinalize(context.forwardPayload.load()); } -/* --- Bounced AskToTransfer during lock --- */ - fun onLockTransferBounced( mutate st: Storage, sender: address, msg: AskToTransfer, ) { - val pendingEntry = st.pendingLocks.get(msg.queryId); - if (!pendingEntry.isFound) { - return; - } - - val pending = pendingEntry.loadValue().load(); - st.pendingLocks.delete(msg.queryId); - - // TODO: try to best-effort return jettons and emit error event - - val fail = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: createAddressNone() as address, // TODO: pending.replyTo!, - body: TokenPool_LockOrBurnFailure { - queryId: msg.queryId, - errorCode: LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce as uint16, - }, - }); - fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + assert(st.poolData.load().jettonClient().isWallet(sender)) throw LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce; + assert(msg.customPayload != null) throw LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce; + val context = lazy LockReleaseLockboxTokenPool_LockContext.fromCell(msg.customPayload!); + returnLockOrBurnTransfer(st, context.forwardPayload.load()); + sendLockOrBurnFailure(context.forwardPayload.load(), LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce as uint16); } /* === Release (withdraw) flow === */ @@ -208,11 +192,11 @@ fun onLockTransferBounced( * Flow: * 1. Router sends TokenPool_ReleaseOrMint * 2. TokenPool library validates, calls onReleaseOrMintContinue - * 3. We store pending release context + send JettonLockBox_Withdraw(sendExcessesTo=us) to lockbox + * 3. We retain the direct lockbox-failure context and deploy an executor for success * 4. Lockbox sends AskToTransfer to jetton wallet - * 5. Jetton wallet transfers to recipient, sends ReturnExcessesBack to pool - * 6. We restore pending context → finalize release flow - * 7. On error: lockbox sends JettonLockBox_WithdrawFailed → we send TokenPool_ReleaseOrMintFailure + * 5. Jetton wallet transfers to recipient, then sends ReturnExcessesBack to the executor + * 6. The executor forwards context and we finalize the release + * 7. On error, the lockbox sends JettonLockBox_WithdrawFailed directly to the pool */ fun onReleaseOrMintContinue( @@ -223,7 +207,7 @@ fun onReleaseOrMintContinue( ): Storage? { assert(ctx != null, TokenPool_Error.UnsupportedOperation); var st = ctx!; - assert(!st.pendingReleases.get(msg.queryId).isFound, LockReleaseLockBoxTokenPool_Error.PendingReleaseAlreadyExists); + assert(st.canTakeContextExecutorNextId()) throw LockReleaseLockBoxTokenPool_Error.ContextExecutorUnavailable; assert(st.lockbox != createAddressNone(), LockReleaseLockBoxTokenPool_Error.LockboxNotConfigured); val jettonClient = st.poolData.load().jettonClient(); @@ -231,27 +215,48 @@ fun onReleaseOrMintContinue( val request = lazy msg.request.load(); val details = lazy request.transfer.details.load(); - // Calculate the recipient's jetton wallet address. + // The CCT JettonWallet treats transferRecipient as an owner address and + // computes the wallet from it, so we pass the raw receiver (owner) address + // to the lockbox. The lockbox forwards it as transferRecipient in AskToTransfer, + // and the CCT wallet correctly computes the recipient's jetton wallet. + // We still need the computed wallet address for the executor's forwardFrom list + // because ReturnExcessesBack comes from the recipient's wallet. val recipientWallet = calcAddressOfJettonWallet( details.receiver, jettonClient.masterAddress, jettonClient.jettonWalletCode, ); - // Store pending release context keyed by queryId. - st.pendingReleases.set(msg.queryId, LockReleaseLockboxTokenPool_PendingRelease { - queryId: msg.queryId, - replyTo: msg.replyTo.toCell(), - request: request.toCell(), - out: prepared.out.toCell(), - }.toCell()); + val forwardPayload = TokenPool_ReleaseOrMintForwardPayload { + originalSender: sender, + requestMsg: msg.toCell(), + prepared: prepared.toCell(), + }; + val context = LockReleaseLockboxTokenPool_ReleaseContext { + forwardPayload: forwardPayload.toCell(), + }; + val contextExecutorId = st.takeContextExecutorNextId(); + // ReturnExcessesBack comes from the recipient's jetton wallet (which processes + // the InternalTransferStep and sends excesses back to sendExcessesTo). + val executor = ContextExecutor.getAddress( + st.contextExecutorCode, + contextExecutorId, + contract.getAddress(), + context.toCell(), + [recipientWallet], + ); + deployLockReleaseLockboxContextExecutor(executor); - // Build extra cell with sendExcessesTo=pool (cell ref to avoid overflow). + // Return excesses are the successful withdrawal completion signal. val extra = JettonLockBox_WithdrawExtra { - sendExcessesTo: contract.getAddress(), + sendExcessesTo: executor.calculateAddress(), + failureContext: context.toCell(), }.toCell(); // Send JettonLockBox_Withdraw to the lockbox. + // The lockbox passes recipientWallet as transferRecipient in AskToTransfer, + // and the CCT JettonWallet computes the wallet from it. So we pass the raw + // receiver (owner) address, not the pre-computed wallet address. val withdraw = createMessage({ bounce: BounceMode.RichBounce, value: ton("0.1"), @@ -261,7 +266,7 @@ fun onReleaseOrMintContinue( token: jettonClient.masterAddress, remoteChainSelector: details.remoteChainSelector, amount: prepared.localAmount, - recipientWallet, + recipientWallet: details.receiver, extra, }, }); @@ -270,97 +275,134 @@ fun onReleaseOrMintContinue( return st; } -/* --- ReturnExcessesBack handler --- */ -/* - * For lock flow: just accept TON, do nothing. Completion comes from JettonLockBox_Deposited. - * For release flow: this IS the completion signal. Restore context, finalize release flow. - */ - -/* --- JettonLockBox_WithdrawFailed callback --- */ +fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { + assert(st.canTakeContextExecutorNextId()) throw LockReleaseLockBoxTokenPool_Error.ContextExecutorUnavailable; + return st; +} fun onWithdrawFailed( mutate st: Storage, sender: address, msg: JettonLockBox_WithdrawFailed, ) { - // Validate sender is our lockbox. assert(sender == st.lockbox, LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender); + assert(msg.context != null, LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender); + val context = lazy LockReleaseLockboxTokenPool_ReleaseContext.fromCell(msg.context!); + assert(msg.queryId == context.forwardPayload.load().requestMsg.load().queryId) throw 0xFFFF; + sendReleaseOrMintFailure(context.forwardPayload.load(), TokenPool_Error.UnsupportedOperation as uint16); +} - val pendingEntry = st.pendingReleases.get(msg.queryId); - if (!pendingEntry.isFound) { - return; +fun onReturnExcessesBack( + _st: Storage, + _sender: address, + _msg: ReturnExcessesBack, +) { + // Lock-transfer excesses are unrelated to completion. Release excesses are + // routed to a Context Executor and never complete directly at the pool. +} + +fun onContextExecutorForwardNotification( + mutate st: Storage, + sender: address, + msg: ContextExecutor_ForwardNotification, +) { + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), TokenPool_Error.Unauthorized); + + val forwarded = msg.message.load(); + val returnExcesses = ReturnExcessesBack.fromCell(forwarded.body); + val operation = msg.context.load().forwardPayload.load(); + assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; + + sendReleaseOrMintFinished(operation); +} + +fun sendLockOrBurnFailure( + forwardPayload: TokenPool_LockOrBurnForwardPayload, + errorCode: uint16, +) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val failure = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_LockOrBurnFailure { queryId: requestMsg.queryId, errorCode }, + }); + failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } +} - val pending = pendingEntry.loadValue().load(); - st.pendingReleases.delete(msg.queryId); +fun returnLockOrBurnTransfer(st: Storage, forwardPayload: TokenPool_LockOrBurnForwardPayload) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val returnTransfer = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: st.poolData.load().jettonClient().walletAddress(), + body: AskToTransfer { + queryId: requestMsg.queryId, + jettonAmount: details.amount, + transferRecipient: details.originalSender, + sendExcessesTo: details.originalSender, + customPayload: null, + forwardTonAmount: 0, + forwardPayload: beginCell().storeBool(false).endCell().beginParse(), + }, + }); + returnTransfer.send(SEND_MODE_PAY_FEES_SEPARATELY); +} - // Send failure notification. - val replyToAddr = pending.replyTo.load(); - if (replyToAddr != null) { - val fail = createMessage({ +fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + + emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { + remoteChainSelector: details.remoteChainSelector, + details: TokenPool_ReleasedOrMintedDetails { + token: details.localToken, + sender: contract.getAddress(), + amount: prepared.out.destinationAmount, + recipient: details.receiver.toCell(), + }.toCell(), + }); + if (requestMsg.replyTo != null) { + val notify = createMessage({ bounce: BounceMode.NoBounce, value: 0, - dest: replyToAddr!, - body: TokenPool_ReleaseOrMintFailure { - queryId: msg.queryId, - errorCode: TokenPool_Error.UnsupportedOperation as uint16, + dest: requestMsg.replyTo!, + body: TokenPool_ReleaseOrMintFinished { + queryId: requestMsg.queryId, + out: prepared.out.toCell(), }, }); - fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } } -/* --- ReturnExcessesBack handler --- */ -/* - * For lock flow: just accept TON, do nothing. Completion comes from JettonLockBox_Deposited. - * For release flow: this IS the completion signal. Restore context, finalize release flow. - */ - -fun onReturnExcessesBack( - mutate st: Storage, - sender: address, - msg: ReturnExcessesBack, +fun sendReleaseOrMintFailure( + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, + errorCode: uint16, ) { - // Check if this is a release completion (pending release exists). - val releaseEntry = st.pendingReleases.get(msg.queryId); - if (releaseEntry.isFound) { - val pending = releaseEntry.loadValue().load(); - st.pendingReleases.delete(msg.queryId); - - val request = lazy pending.request.load(); - val details = lazy request.transfer.details.load(); - val out = lazy pending.out.load(); - - // Emit release event. - emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { - remoteChainSelector: details.remoteChainSelector, - details: TokenPool_ReleasedOrMintedDetails { - token: details.localToken, - sender: contract.getAddress(), - amount: out.destinationAmount, - recipient: details.receiver.toCell(), - }.toCell(), + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val failure = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_ReleaseOrMintFailure { queryId: requestMsg.queryId, errorCode }, }); - - // Send completion notification if replyTo is set. - val replyToAddr = pending.replyTo.load(); - if (replyToAddr != null) { - val notify = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: replyToAddr!, - body: TokenPool_ReleaseOrMintFinished { - queryId: msg.queryId, - out: pending.out, - }, - }); - notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } - return; + failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } - - // Otherwise it's from the lock flow (ReturnExcessesBack from jetton wallet after deposit). - // Just accept TON, do nothing. Real completion comes from JettonLockBox_Deposited. } /* === Pool loader === */ @@ -378,7 +420,7 @@ fun loadPool(st: Storage): TokenPool { preflightCheck: null, onLockOrBurnTransfer: null, onLockOrBurnTransferContinue, - onReleaseOrMint: null, + onReleaseOrMint, validateReleaseOrMint: null, postflightCheck: null, onReleaseOrMintContinue, @@ -418,14 +460,6 @@ get fun offRamp(remoteChainSelector: uint64): address? { return entry.isFound ? entry.loadValue() : null; } -get fun hasPendingLock(queryId: uint64): bool { - return Storage.load().pendingLocks.get(queryId).isFound; -} - -get fun hasPendingRelease(queryId: uint64): bool { - return Storage.load().pendingReleases.get(queryId).isFound; -} - get fun getRMNProxy(): address { return Storage.load().poolData.load().adminConfig.load().rmnProxy; } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk index d43cc82e1..487f0db04 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk @@ -4,10 +4,7 @@ const LockReleaseLockboxTokenPool_FACILITY_NAME = "link.chain.ton.ccip.LockRelea const LockReleaseLockboxTokenPool_FACILITY_ID = 487; // (crc32() % 640) + 10 enum LockReleaseLockBoxTokenPool_Error { - PendingLockAlreadyExists = LockReleaseLockboxTokenPool_FACILITY_ID * 100 - PendingLockNotFound - PendingReleaseAlreadyExists - PendingReleaseNotFound + ContextExecutorUnavailable = LockReleaseLockboxTokenPool_FACILITY_ID * 100 LockboxNotConfigured UnexpectedLockboxConfirmationSender diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk index aff7e186a..6ae3fd7be 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk @@ -1,16 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 import "../../../lib/jetton/messages" +import "../../executors/context_executor/messages" import "../lockbox/messages" +import "types" -// Messages we receive from lockbox or jetton system: -// - JettonLockBox_Deposited: lockbox confirms deposit (lock flow completion) -// - JettonLockBox_WithdrawFailed: lockbox reports withdraw failure (release error) -// - ReturnExcessesBack: completion signal for both lock and release flows type LockReleaseLockboxTokenPool_InMessage = | ReturnExcessesBack | JettonLockBox_Deposited - | JettonLockBox_WithdrawFailed; + | JettonLockBox_WithdrawFailed + | ContextExecutor_ForwardNotification; type LockReleaseLockboxTokenPool_OutMessage = | AskToTransfer; diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk index 279155c04..247fd5bb4 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk @@ -10,9 +10,21 @@ import "types" struct Storage { poolData: Cell; lockbox: address; - // Pending contexts keyed by queryId for async flow completion. - pendingLocks: map> = []; - pendingReleases: map> = []; + contextExecutorCode: cell; + contextExecutorNextId: uint64; +} + +@inline +fun Storage.canTakeContextExecutorNextId(self): bool { + return self.contextExecutorNextId != 18446744073709551615; +} + +@inline +fun Storage.takeContextExecutorNextId(mutate self): uint64 { + val id = self.contextExecutorNextId; + assert(id != 18446744073709551615) throw 0xFFFF; + self.contextExecutorNextId = id + 1; + return id; } fun Storage.load(): Storage { @@ -54,7 +66,7 @@ fun Storage.init(config: LockReleaseLockboxTokenPool_InitConfig): Storage { tokenTransferFeeConfigs: createEmptyMap(), }.toCell(), lockbox: config.lockbox, - pendingLocks: createEmptyMap(), - pendingReleases: createEmptyMap(), + contextExecutorCode: config.contextExecutorCode, + contextExecutorNextId: 1, }; } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk index 69494081f..9301e734d 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk @@ -7,25 +7,20 @@ import "../lib/token_pool/messages" const LockReleaseLockboxTokenPool_CONTRACT_NAME = "link.chain.ton.ccip.LockReleaseLockboxTokenPool".literalSlice(); const LockReleaseLockboxTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); -// Pending lock context stored by queryId until lockbox confirms deposit. -// On JettonLockBox_Deposited, pool restores this and finalizes the lock flow. -struct LockReleaseLockboxTokenPool_PendingLock { - queryId: uint64; +const LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); + +struct (0x4b92a8e8) LockReleaseLockboxTokenPool_LockContext { forwardPayload: Cell; } -// Pending release context stored by queryId until ReturnExcessesBack arrives. -// On ReturnExcessesBack, pool restores this and finalizes the release flow. -struct LockReleaseLockboxTokenPool_PendingRelease { - queryId: uint64; - replyTo: Cell; - request: Cell; - out: Cell; +struct (0xad5476ce) LockReleaseLockboxTokenPool_ReleaseContext { + forwardPayload: Cell; } struct LockReleaseLockboxTokenPool_InitConfig { owner: address; jettonClient: JettonClient; + contextExecutorCode: cell; tokenDecimals: uint8; router: address; rmnProxy: address; diff --git a/contracts/contracts/ccip/pools/lockbox/JettonLockBox.tolk b/contracts/contracts/ccip/pools/lockbox/JettonLockBox.tolk index 23e9dac27..4772230a4 100644 --- a/contracts/contracts/ccip/pools/lockbox/JettonLockBox.tolk +++ b/contracts/contracts/ccip/pools/lockbox/JettonLockBox.tolk @@ -155,7 +155,6 @@ fun onDepositNotification(mutate st: Storage, sender: address, msg: TransferNoti // Emit deposit event. emit(TOPIC_DEPOSIT, fwdp); - // TODO: needs to include more context/fwdPayload val reply = createMessage({ bounce: BounceMode.NoBounce, value: 0, @@ -165,6 +164,7 @@ fun onDepositNotification(mutate st: Storage, sender: address, msg: TransferNoti token: fwdp.token, remoteChainSelector: fwdp.remoteChainSelector, amount: fwdp.amount, + context: fwdp.context, }, }); reply.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); @@ -193,6 +193,7 @@ fun onWithdraw(mutate st: Storage, sender: address, msg: JettonLockBox_Withdraw) .storeUint(0x17c24007, 32) // JettonLockBox_WithdrawRequest marker .storeUint(msg.queryId, 64) .storeAddress(sender) + .storeMaybeRef(msg.extra != null ? msg.extra!.load().failureContext : null) .endCell(); val fwdPayload = beginCell() @@ -233,7 +234,6 @@ fun onWithdraw(mutate st: Storage, sender: address, msg: JettonLockBox_Withdraw) } /// Handle bounced AskToTransfer (withdraw failed) - send error message to initiator. -/// The pool stores pending releases by queryId and restores context on JettonLockBox_WithdrawFailed. fun onBouncedMessage(in: InMessageBounced) { val st = lazy Storage.load(); @@ -253,6 +253,7 @@ fun onBouncedMessage(in: InMessageBounced) { val marker = bounceData.loadUint(32); val queryId = bounceData.loadUint(64); val withdrawInitiator = bounceData.loadAddress(); + val failureContext = bounceData.loadMaybeRef(); // Send error message to withdraw initiator val errorMsg = createMessage({ @@ -264,6 +265,7 @@ fun onBouncedMessage(in: InMessageBounced) { token: st.minterAddress, amount: msg.jettonAmount, recipientWallet: msg.transferRecipient, + context: failureContext, }, }); errorMsg.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); diff --git a/contracts/contracts/ccip/pools/lockbox/messages.tolk b/contracts/contracts/ccip/pools/lockbox/messages.tolk index 4a0a822da..ec4700e0d 100644 --- a/contracts/contracts/ccip/pools/lockbox/messages.tolk +++ b/contracts/contracts/ccip/pools/lockbox/messages.tolk @@ -11,11 +11,13 @@ struct (0x9e9ec361) JettonLockBox_Deposit { token: address; remoteChainSelector: uint64; amount: coins; + context: cell?; } /// Optional extra fields for JettonLockBox_Withdraw (stored in a cell ref to avoid overflow). struct JettonLockBox_WithdrawExtra { sendExcessesTo: address?; + failureContext: cell?; } /// Withdraws tokens to a specific recipient. @@ -42,6 +44,7 @@ struct (0x6d077f2e) JettonLockBox_Deposited { token: address; remoteChainSelector: uint64; amount: coins; + context: cell?; } /// Contract initialization message. @@ -75,6 +78,7 @@ struct (0x60bae556) JettonLockBox_WithdrawFailed { token: address; amount: coins; recipientWallet: address; + context: cell?; } /// Union of all incoming messages this contract handles. diff --git a/contracts/tests/ccip/pools/JettonLockbox.spec.ts b/contracts/tests/ccip/pools/JettonLockbox.spec.ts index 49a028ae2..bb4d089db 100644 --- a/contracts/tests/ccip/pools/JettonLockbox.spec.ts +++ b/contracts/tests/ccip/pools/JettonLockbox.spec.ts @@ -192,6 +192,7 @@ describe('JettonLockBox', () => { token: jettonMinter.address, remoteChainSelector, amount, + context: null, }), ) @@ -248,6 +249,7 @@ describe('JettonLockBox', () => { token: jettonMinter.address, remoteChainSelector, amount: 0n, + context: null, }), ) @@ -304,6 +306,7 @@ describe('JettonLockBox', () => { token: jettonMinter.address, remoteChainSelector, amount, + context: null, }), ) @@ -363,6 +366,7 @@ describe('JettonLockBox', () => { token: jettonMinter.address, remoteChainSelector, amount: depositAmount, + context: null, }), ) diff --git a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts index e1acbf943..723fd7402 100644 --- a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts @@ -14,7 +14,6 @@ import { TokenPool_MirroredPolicy, TokenPool_ReleaseOrMintFinished, TokenPool_LockOrBurn, - TokenPool_LockOrBurnFailure, TokenPool_LockOrBurnForwardPayload, TokenPool_LockOrBurnPrepared, TokenPool_LockOrBurnOutV1, @@ -35,6 +34,10 @@ import { JettonLockBox, JettonLockBox_WithdrawExtra, } from '../../../wrappers/gen/ccip/pools/JettonLockBox' +import { + ContextExecutor, + ContextExecutor_ForwardNotification, +} from '../../../wrappers/gen/ccip/ContextExecutor' import { ContractClient as AccessControlClient } from '../../../wrappers/lib/access/AccessControl' import { runTokenPoolBehaviorTests, runTokenPoolAsyncHookBehaviorTests } from './TokenPool.behavior' @@ -162,8 +165,8 @@ describe('LockReleaseLockboxTokenPool', () => { tokenTransferFeeConfigs: new Map(), }), lockbox: jettonLockBox.address, - pendingLocks: new Map(), - pendingReleases: new Map(), + contextExecutorCode: ContextExecutor.CodeCell, + contextExecutorNextId: 1n, }), ) await lockReleaseLockboxPool.sendDeploy(deployer.getSender(), toNano('5')) @@ -350,13 +353,6 @@ describe('LockReleaseLockboxTokenPool', () => { expect(await lockReleaseLockboxPool.getTokenDecimals()).toBe(9n) }) - it('should have no pending lock by default', async () => { - expect(await lockReleaseLockboxPool.getHasPendingLock(999n)).toBe(false) - }) - - it('should have no pending release by default', async () => { - expect(await lockReleaseLockboxPool.getHasPendingRelease(999n)).toBe(false) - }) }) describe('lock flow (jetton transfer -> lockbox custody)', () => { @@ -417,8 +413,6 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) - // Pool should have a pending lock - expect(await lockReleaseLockboxPool.getHasPendingLock(100n)).toBe(true) }) it('should store pending lock when forwarded amount matches transfer amount', async () => { @@ -470,7 +464,6 @@ describe('LockReleaseLockboxTokenPool', () => { }, }) - expect(await lockReleaseLockboxPool.getHasPendingLock(101n)).toBe(true) }) }) @@ -523,8 +516,6 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) - // Pool should have a pending release - expect(await lockReleaseLockboxPool.getHasPendingRelease(200n)).toBe(true) // Pool sends JettonLockBox_Withdraw to the lockbox (lockbox will handle the jetton transfer) expect(result.transactions).toHaveTransaction({ @@ -535,91 +526,6 @@ describe('LockReleaseLockboxTokenPool', () => { }) }) - it('should reject duplicate release requests with PendingReleaseAlreadyExists', async () => { - // Fund lockbox first - const lockboxWalletAddress = await jettonMinter.getWalletAddress(jettonLockBox.address) - await jettonMinter.sendMint(deployer.getSender(), { - value: toNano('1'), - message: { - queryId: 0n, - destination: lockboxWalletAddress, - tonAmount: toNano('0.05'), - jettonAmount: toNano('50'), - from: deployer.address, - responseDestination: deployer.address, - forwardTonAmount: 0n, - }, - }) - - // First release request - should succeed - const firstResult = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), - toNano('0.5'), - { - queryId: 220n, - request: TokenPool_ReleaseOrMintInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 220n, - details: TokenPool_TransferDetails.create({ - originalSender: sourcePoolAddress, - remoteChainSelector, - receiver: recipient.address, - amount: toNano('5'), - localToken: jettonMinter.address, - }), - }), - sourcePoolAddress: sourcePoolAddress, - sourcePoolData: null, - offchainTokenData: null, - }), - requestedFinalityConfig: 0n, - replyTo: deployer.address, - }, - ) - - expect(firstResult.transactions).toHaveTransaction({ - to: lockReleaseLockboxPool.address, - success: true, - }) - expect(await lockReleaseLockboxPool.getHasPendingRelease(220n)).toBe(true) - - // Second release with same queryId - should be rejected - const secondResult = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( - offRamp.getSender(), - toNano('0.5'), - { - queryId: 220n, - request: TokenPool_ReleaseOrMintInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 220n, - details: TokenPool_TransferDetails.create({ - originalSender: sourcePoolAddress, - remoteChainSelector, - receiver: recipient.address, - amount: toNano('5'), - localToken: jettonMinter.address, - }), - }), - sourcePoolAddress: sourcePoolAddress, - sourcePoolData: null, - offchainTokenData: null, - }), - requestedFinalityConfig: 0n, - replyTo: deployer.address, - }, - ) - - // Exit code 48702 = PendingReleaseAlreadyExists - expect(secondResult.transactions).toHaveTransaction({ - to: lockReleaseLockboxPool.address, - success: false, - exitCode: 48702, - }) - - // Original pending release should still exist - expect(await lockReleaseLockboxPool.getHasPendingRelease(220n)).toBe(true) - }) - it('should reject release when requested amount exceeds lockbox liquidity', async () => { const result = await lockReleaseLockboxPool.sendTokenPoolReleaseOrMint( offRamp.getSender(), @@ -651,7 +557,6 @@ describe('LockReleaseLockboxTokenPool', () => { to: lockReleaseLockboxPool.address, success: false, }) - expect(await lockReleaseLockboxPool.getHasPendingRelease(201n)).toBe(false) }) }) @@ -745,121 +650,8 @@ describe('LockReleaseLockboxTokenPool', () => { op: 0x178d4519, // TransferNotificationForRecipient success: true, }) - - // Pool stores pending lock after receiving TransferNotificationForRecipient - expect(await lockReleaseLockboxPool.getHasPendingLock(300n)).toBe(true) - - // The pool computes the lockbox wallet address and sends AskToTransfer to forward jettons - // In the sandbox, the lockbox wallet is a standard jetton wallet (not JettonLockBox contract). - // It returns excesses but does NOT send JettonLockBox_Deposited back to finalize. - // In production, the JettonLockBox contract would process the deposit and send the callback, - // clearing the pending lock and emitting TokenPool_LockedOrBurned. }) - it('returns duplicate lock transfers and notifies the requester', async () => { - // The pending lock from the first request makes the second continuation fail. - // The shared TP path must recover those already-received jettons. - - const onRampWallet = await userWallet(deployer.address) - const poolWallet = await userWallet(lockReleaseLockboxPool.address) - - const lockOrBurn = TokenPool_LockOrBurn.create({ - queryId: 310n, - request: TokenPool_LockOrBurnInV1.create({ - transfer: TokenPool_Transfer.create({ - id: 310n, - details: TokenPool_TransferDetails.create({ - receiver: receiverAddress, - remoteChainSelector, - originalSender: deployer.address, - amount: toNano('5'), - localToken: jettonMinter.address, - }), - }), - }), - requestedFinalityConfig: 0n, - tokenArgs: null, - replyTo: deployer.address, - }) - - const forwardPayload = TokenPool_LockOrBurnForwardPayload.create({ - originalSender: deployer.address, - requestMsg: lockOrBurn, - prepared: TokenPool_LockOrBurnPrepared.create({ - feeAmount: 0n, - destTokenAmount: toNano('5'), - out: TokenPool_LockOrBurnOutV1.create({ - destTokenAddress: destTokenAddress, - destPoolData: Cell.EMPTY, - }), - }), - }) - - // First lock request - should succeed - const firstResult = await onRampWallet.sendTransfer(deployer.getSender(), { - value: toNano('3'), - message: { - queryId: 310, - jettonAmount: toNano('5'), - destination: lockReleaseLockboxPool.address, - responseDestination: deployer.address, - customPayload: null, - forwardTonAmount: toNano('0.5'), - forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), - }, - }) - - expect(firstResult.transactions).toHaveTransaction({ - to: lockReleaseLockboxPool.address, - success: true, - }) - expect(await lockReleaseLockboxPool.getHasPendingLock(310n)).toBe(true) - - // Second lock request with the same queryId is returned by the shared continuation path. - const secondResult = await onRampWallet.sendTransfer(deployer.getSender(), { - value: toNano('3'), - message: { - queryId: 310, - jettonAmount: toNano('5'), - destination: lockReleaseLockboxPool.address, - responseDestination: deployer.address, - customPayload: null, - forwardTonAmount: toNano('0.5'), - forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), - }, - }) - - expect(secondResult.transactions).toHaveTransaction({ - from: poolWallet.address, - to: lockReleaseLockboxPool.address, - success: true, - }) - expect(secondResult.transactions).toHaveTransaction({ - from: lockReleaseLockboxPool.address, - to: poolWallet.address, - success: true, - op: 0x0f8a7ea5, // AskToTransfer - }) - expect(secondResult.transactions).toHaveTransaction({ - from: lockReleaseLockboxPool.address, - to: deployer.address, - // The Treasury test recipient does not implement this callback. Assert - // the emitted message, including that it carries the residual inbound - // value instead of consuming it in the pool. - op: TokenPool_LockOrBurnFailure.PREFIX, - value: (value) => value !== undefined && value > 0n, - body(body) { - if (!body) return false - const failure = TokenPool_LockOrBurnFailure.fromSlice(body.beginParse()) - return failure.queryId === 310n && failure.errorCode === 48700n - }, - }) - - // Original pending lock should still exist - expect(await lockReleaseLockboxPool.getHasPendingLock(310n)).toBe(true) - expect(await onRampWallet.getJettonBalance()).toEqual(toNano('995')) - expect(await poolWallet.getJettonBalance()).toEqual(0n) - }) }) describe('full release flow (end-to-end through lockbox)', () => { @@ -941,18 +733,13 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) - // Recipient's wallet sends ReturnExcessesBack to the pool + // ReturnExcessesBack is routed through the context executor before the + // pool finalizes the release and clears the direct failure context. expect(result.transactions).toHaveTransaction({ to: lockReleaseLockboxPool.address, - op: 0xd53276db, // ReturnExcessesBack + op: ContextExecutor_ForwardNotification.PREFIX, success: true, }) - - // Pending release should be cleared (release completed) - expect(await lockReleaseLockboxPool.getHasPendingRelease(400n)).toBe(false) - - // Release flow complete: offRamp -> pool -> lockbox -> lockbox wallet -> TransferNotification -> recipient wallet - // Recipient wallet sends ReturnExcessesBack to pool confirming the transfer. }) it('should handle release flow failure when lockbox has insufficient balance', async () => { @@ -992,7 +779,6 @@ describe('LockReleaseLockboxTokenPool', () => { }) // No pending release should exist (either rejected upfront or cleaned up) - expect(await lockReleaseLockboxPool.getHasPendingRelease(401n)).toBe(false) }) }) diff --git a/contracts/wrappers/gen/ccip/pools/JettonLockBox.ts b/contracts/wrappers/gen/ccip/pools/JettonLockBox.ts index 5f84f83c7..11f600558 100644 --- a/contracts/wrappers/gen/ccip/pools/JettonLockBox.ts +++ b/contracts/wrappers/gen/ccip/pools/JettonLockBox.ts @@ -386,6 +386,7 @@ export const Storage = { > token: address > remoteChainSelector: uint64 > amount: coins + > context: cell? > } */ export interface JettonLockBox_Deposit { @@ -394,6 +395,7 @@ export interface JettonLockBox_Deposit { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null } export const JettonLockBox_Deposit = { @@ -404,6 +406,7 @@ export const JettonLockBox_Deposit = { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null }): JettonLockBox_Deposit { return { $: 'JettonLockBox_Deposit', @@ -419,6 +422,7 @@ export const JettonLockBox_Deposit = { token: s.loadAddress(), remoteChainSelector: s.loadUintBig(64), amount: s.loadCoins(), + context: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_Deposit, b: c.Builder): void { @@ -427,6 +431,9 @@ export const JettonLockBox_Deposit = { b.storeAddress(self.token); b.storeUint(self.remoteChainSelector, 64); b.storeCoins(self.amount); + storeTolkNullable(self.context, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_Deposit): c.Cell { return makeCellFrom(self, JettonLockBox_Deposit.store); @@ -436,16 +443,19 @@ export const JettonLockBox_Deposit = { /** > struct JettonLockBox_WithdrawExtra { > sendExcessesTo: address? + > failureContext: cell? > } */ export interface JettonLockBox_WithdrawExtra { readonly $: 'JettonLockBox_WithdrawExtra' sendExcessesTo: c.Address | null + failureContext: c.Cell | null } export const JettonLockBox_WithdrawExtra = { create(args: { sendExcessesTo: c.Address | null + failureContext: c.Cell | null }): JettonLockBox_WithdrawExtra { return { $: 'JettonLockBox_WithdrawExtra', @@ -456,10 +466,14 @@ export const JettonLockBox_WithdrawExtra = { return { $: 'JettonLockBox_WithdrawExtra', sendExcessesTo: s.loadMaybeAddress(), + failureContext: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_WithdrawExtra, b: c.Builder): void { b.storeAddress(self.sendExcessesTo); + storeTolkNullable(self.failureContext, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_WithdrawExtra): c.Cell { return makeCellFrom(self, JettonLockBox_WithdrawExtra.store); @@ -537,6 +551,7 @@ export const JettonLockBox_Withdraw = { > token: address > remoteChainSelector: uint64 > amount: coins + > context: cell? > } */ export interface JettonLockBox_Deposited { @@ -545,6 +560,7 @@ export interface JettonLockBox_Deposited { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null } export const JettonLockBox_Deposited = { @@ -555,6 +571,7 @@ export const JettonLockBox_Deposited = { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null }): JettonLockBox_Deposited { return { $: 'JettonLockBox_Deposited', @@ -570,6 +587,7 @@ export const JettonLockBox_Deposited = { token: s.loadAddress(), remoteChainSelector: s.loadUintBig(64), amount: s.loadCoins(), + context: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_Deposited, b: c.Builder): void { @@ -578,6 +596,9 @@ export const JettonLockBox_Deposited = { b.storeAddress(self.token); b.storeUint(self.remoteChainSelector, 64); b.storeCoins(self.amount); + storeTolkNullable(self.context, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_Deposited): c.Cell { return makeCellFrom(self, JettonLockBox_Deposited.store); @@ -696,6 +717,7 @@ export const JettonLockBox_Initialized = { > token: address > amount: coins > recipientWallet: address + > context: cell? > } */ export interface JettonLockBox_WithdrawFailed { @@ -704,6 +726,7 @@ export interface JettonLockBox_WithdrawFailed { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null } export const JettonLockBox_WithdrawFailed = { @@ -714,6 +737,7 @@ export const JettonLockBox_WithdrawFailed = { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null }): JettonLockBox_WithdrawFailed { return { $: 'JettonLockBox_WithdrawFailed', @@ -729,6 +753,7 @@ export const JettonLockBox_WithdrawFailed = { token: s.loadAddress(), amount: s.loadCoins(), recipientWallet: s.loadAddress(), + context: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_WithdrawFailed, b: c.Builder): void { @@ -737,6 +762,9 @@ export const JettonLockBox_WithdrawFailed = { b.storeAddress(self.token); b.storeCoins(self.amount); b.storeAddress(self.recipientWallet); + storeTolkNullable(self.context, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_WithdrawFailed): c.Cell { return makeCellFrom(self, JettonLockBox_WithdrawFailed.store); @@ -782,7 +810,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class JettonLockBox implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'AccessControl_Error.UnauthorizedAccount': 47400, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 3af8d936f..de7a01fb9 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -11,6 +11,8 @@ import { beginCell, ContractProvider, Sender, SendMode } from '@ton/core'; type RemainingBitsAndRefs = c.Slice +type array = T[] + // TypeScript wrappers flatten a TVM linked list `[1 [2 [3 null]]]` to `[1 2 3]` type lisp_list = T[] @@ -106,6 +108,35 @@ function storeTolkNullable(v: T | null, b: c.Builder, storeFn_T: StoreCallbac } } +function storeArrayOf(v: array, b: c.Builder, storeFn_T: StoreCallback): void { + // the compiler stores array in chunks; in TypeScript, for simplicity, store "1 elem = 1 ref" + let tail = null as c.Cell | null; + for (let i = 0; i < v.length; ++i) { + let chunkB = beginCell().storeMaybeRef(tail); + storeFn_T(v[v.length - 1 - i], chunkB); + tail = chunkB.endCell(); + } + b.storeUint(v.length, 8); + b.storeMaybeRef(tail); +} + +function loadArrayOf(s: c.Slice, loadFn_T: LoadCallback): array { + let len = s.loadUint(8); + let head = s.loadMaybeRef(); + let outArr = [] as array; + while (head != null) { + let s = head.beginParse(); + head = s.loadMaybeRef(); + while (s.remainingBits || s.remainingRefs) { + outArr.push(loadFn_T(s)); + } + } + if (len !== outArr.length) { + throw new Error(`mismatch array binary data: expected ${len} elements, got ${outArr.length}`); + } + return outArr; +} + function storeLispListOf(v: lisp_list, b: c.Builder, storeFn_T: StoreCallback): void { let tail = c.Cell.EMPTY; for (let i = 0; i < v.length; ++i) { @@ -278,6 +309,8 @@ function invokeCustomUnpackFromSlice(typeName: string, s: c.Slice): T { type coins = bigint +type int32 = bigint + type uint8 = bigint type uint16 = bigint type uint32 = bigint @@ -286,6 +319,34 @@ type uint120 = bigint type uint128 = bigint type uint256 = bigint +type varuint32 = bigint + +/** + > type ExtraCurrenciesMap = map + */ +export type ExtraCurrenciesMap = Map + +export const ExtraCurrenciesMap = { + fromSlice(s: c.Slice): ExtraCurrenciesMap { + return dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + ), s)); + }, + store(self: ExtraCurrenciesMap, b: c.Builder): void { + b.storeDict(mapToDict(self, c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )), c.Dictionary.Keys.BigInt(32), createDictionaryValue( + (s) => s.loadVarUintBig(5), + (v,b) => b.storeVarUint(v, 5) + )); + }, + toCell(self: ExtraCurrenciesMap): c.Cell { + return makeCellFrom(self, ExtraCurrenciesMap.store); + } +} + /** > type SnakedCell = cell */ @@ -566,6 +627,101 @@ export const CrossChainAddress = { } } +/** + > struct (0x55b412b9) ContextExecutor_ForwardNotification { + > id: uint64 + > context: Cell + > forwardFrom: array
+ > message: Cell + > } + */ +export interface ContextExecutor_ForwardNotification { + readonly $: 'ContextExecutor_ForwardNotification' + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward +} + +export const ContextExecutor_ForwardNotification = { + PREFIX: 0x55b412b9, + + create(args: { + id: uint64 + context: T + forwardFrom: array + message: ContextExecutor_InMessageForward + }): ContextExecutor_ForwardNotification { + return { + $: 'ContextExecutor_ForwardNotification', + ...args + } + }, +} + +/** + > struct ContextExecutor_InMessageForward { + > senderAddress: address + > valueCoins: coins + > valueExtra: ExtraCurrenciesMap + > originalForwardFee: coins + > createdLt: uint64 + > createdAt: uint32 + > body: cell + > } + */ +export interface ContextExecutor_InMessageForward { + readonly $: 'ContextExecutor_InMessageForward' + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell +} + +export const ContextExecutor_InMessageForward = { + create(args: { + senderAddress: c.Address + valueCoins: coins + valueExtra: ExtraCurrenciesMap + originalForwardFee: coins + createdLt: uint64 + createdAt: uint32 + body: c.Cell + }): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + ...args + } + }, + fromSlice(s: c.Slice): ContextExecutor_InMessageForward { + return { + $: 'ContextExecutor_InMessageForward', + senderAddress: s.loadAddress(), + valueCoins: s.loadCoins(), + valueExtra: ExtraCurrenciesMap.fromSlice(s), + originalForwardFee: s.loadCoins(), + createdLt: s.loadUintBig(64), + createdAt: s.loadUintBig(32), + body: s.loadRef(), + } + }, + store(self: ContextExecutor_InMessageForward, b: c.Builder): void { + b.storeAddress(self.senderAddress); + b.storeCoins(self.valueCoins); + ExtraCurrenciesMap.store(self.valueExtra, b); + b.storeCoins(self.originalForwardFee); + b.storeUint(self.createdLt, 64); + b.storeUint(self.createdAt, 32); + b.storeRef(self.body); + }, + toCell(self: ContextExecutor_InMessageForward): c.Cell { + return makeCellFrom(self, ContextExecutor_InMessageForward.store); + } +} + /** > struct CursedSubjects { > data: map @@ -4101,94 +4257,39 @@ export const TokenPool_RampAccessUpdated = { } /** - > struct LockReleaseLockboxTokenPool_PendingLock { - > queryId: uint64 - > forwardPayload: Cell + > struct (0xad5476ce) LockReleaseLockboxTokenPool_ReleaseContext { + > forwardPayload: Cell > } */ -export interface LockReleaseLockboxTokenPool_PendingLock { - readonly $: 'LockReleaseLockboxTokenPool_PendingLock' - queryId: uint64 - forwardPayload: TokenPool_LockOrBurnForwardPayload -} - -export const LockReleaseLockboxTokenPool_PendingLock = { - create(args: { - queryId?: uint64 - forwardPayload: TokenPool_LockOrBurnForwardPayload - }): LockReleaseLockboxTokenPool_PendingLock { - return { - $: 'LockReleaseLockboxTokenPool_PendingLock', - ...args, - queryId: args.queryId ?? 0n - } - }, - fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_PendingLock { - return { - $: 'LockReleaseLockboxTokenPool_PendingLock', - queryId: s.loadUintBig(64), - forwardPayload: loadCellRef(s, TokenPool_LockOrBurnForwardPayload.fromSlice), - } - }, - store(self: LockReleaseLockboxTokenPool_PendingLock, b: c.Builder): void { - b.storeUint(self.queryId, 64); - storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); - }, - toCell(self: LockReleaseLockboxTokenPool_PendingLock): c.Cell { - return makeCellFrom(self, LockReleaseLockboxTokenPool_PendingLock.store); - } +export interface LockReleaseLockboxTokenPool_ReleaseContext { + readonly $: 'LockReleaseLockboxTokenPool_ReleaseContext' + forwardPayload: TokenPool_ReleaseOrMintForwardPayload } -/** - > struct LockReleaseLockboxTokenPool_PendingRelease { - > queryId: uint64 - > replyTo: Cell - > request: Cell - > out: Cell - > } - */ -export interface LockReleaseLockboxTokenPool_PendingRelease { - readonly $: 'LockReleaseLockboxTokenPool_PendingRelease' - queryId: uint64 - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 -} +export const LockReleaseLockboxTokenPool_ReleaseContext = { + PREFIX: 0xad5476ce, -export const LockReleaseLockboxTokenPool_PendingRelease = { create(args: { - queryId?: uint64 - replyTo: c.Address | null - request: TokenPool_ReleaseOrMintInV1 - out: TokenPool_ReleaseOrMintOutV1 - }): LockReleaseLockboxTokenPool_PendingRelease { + forwardPayload: TokenPool_ReleaseOrMintForwardPayload + }): LockReleaseLockboxTokenPool_ReleaseContext { return { - $: 'LockReleaseLockboxTokenPool_PendingRelease', - ...args, - queryId: args.queryId ?? 0n + $: 'LockReleaseLockboxTokenPool_ReleaseContext', + ...args } }, - fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_PendingRelease { + fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_ReleaseContext { + loadAndCheckPrefix32(s, 0xad5476ce, 'LockReleaseLockboxTokenPool_ReleaseContext'); return { - $: 'LockReleaseLockboxTokenPool_PendingRelease', - queryId: s.loadUintBig(64), - replyTo: loadCellRef(s, - (s) => s.loadMaybeAddress() - ), - request: loadCellRef(s, TokenPool_ReleaseOrMintInV1.fromSlice), - out: loadCellRef(s, TokenPool_ReleaseOrMintOutV1.fromSlice), + $: 'LockReleaseLockboxTokenPool_ReleaseContext', + forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, - store(self: LockReleaseLockboxTokenPool_PendingRelease, b: c.Builder): void { - b.storeUint(self.queryId, 64); - storeCellRef(self.replyTo, b, - (v,b) => b.storeAddress(v) - ); - storeCellRef(self.request, b, TokenPool_ReleaseOrMintInV1.store); - storeCellRef(self.out, b, TokenPool_ReleaseOrMintOutV1.store); + store(self: LockReleaseLockboxTokenPool_ReleaseContext, b: c.Builder): void { + b.storeUint(0xad5476ce, 32); + storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, - toCell(self: LockReleaseLockboxTokenPool_PendingRelease): c.Cell { - return makeCellFrom(self, LockReleaseLockboxTokenPool_PendingRelease.store); + toCell(self: LockReleaseLockboxTokenPool_ReleaseContext): c.Cell { + return makeCellFrom(self, LockReleaseLockboxTokenPool_ReleaseContext.store); } } @@ -4196,24 +4297,24 @@ export const LockReleaseLockboxTokenPool_PendingRelease = { > struct Storage { > poolData: Cell > lockbox: address - > pendingLocks: map> - > pendingReleases: map> + > contextExecutorCode: cell + > contextExecutorNextId: uint64 > } */ export interface Storage { readonly $: 'Storage' poolData: TokenPool_Data lockbox: c.Address - pendingLocks: Map /* = [] as map> */ - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 } export const Storage = { create(args: { poolData: TokenPool_Data lockbox: c.Address - pendingLocks: Map /* = [] as map> */ - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }): Storage { return { $: 'Storage', @@ -4225,33 +4326,15 @@ export const Storage = { $: 'Storage', poolData: loadCellRef(s, TokenPool_Data.fromSlice), lockbox: s.loadAddress(), - pendingLocks: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingLock.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingLock.store) - ), s)), - pendingReleases: dictToMap(c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingRelease.store) - ), s)), + contextExecutorCode: s.loadRef(), + contextExecutorNextId: s.loadUintBig(64), } }, store(self: Storage, b: c.Builder): void { storeCellRef(self.poolData, b, TokenPool_Data.store); b.storeAddress(self.lockbox); - b.storeDict(mapToDict(self.pendingLocks, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingLock.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingLock.store) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingLock.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingLock.store) - )); - b.storeDict(mapToDict(self.pendingReleases, c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingRelease.store) - )), c.Dictionary.Keys.BigUint(64), createDictionaryValue( - (s) => loadCellRef(s, LockReleaseLockboxTokenPool_PendingRelease.fromSlice), - (v,b) => storeCellRef(v, b, LockReleaseLockboxTokenPool_PendingRelease.store) - )); + b.storeRef(self.contextExecutorCode); + b.storeUint(self.contextExecutorNextId, 64); }, toCell(self: Storage): c.Cell { return makeCellFrom(self, Storage.store); @@ -4261,16 +4344,19 @@ export const Storage = { /** > struct JettonLockBox_WithdrawExtra { > sendExcessesTo: address? + > failureContext: cell? > } */ export interface JettonLockBox_WithdrawExtra { readonly $: 'JettonLockBox_WithdrawExtra' sendExcessesTo: c.Address | null + failureContext: c.Cell | null } export const JettonLockBox_WithdrawExtra = { create(args: { sendExcessesTo: c.Address | null + failureContext: c.Cell | null }): JettonLockBox_WithdrawExtra { return { $: 'JettonLockBox_WithdrawExtra', @@ -4281,10 +4367,14 @@ export const JettonLockBox_WithdrawExtra = { return { $: 'JettonLockBox_WithdrawExtra', sendExcessesTo: s.loadMaybeAddress(), + failureContext: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_WithdrawExtra, b: c.Builder): void { b.storeAddress(self.sendExcessesTo); + storeTolkNullable(self.failureContext, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_WithdrawExtra): c.Cell { return makeCellFrom(self, JettonLockBox_WithdrawExtra.store); @@ -4362,6 +4452,7 @@ export const JettonLockBox_Withdraw = { > token: address > remoteChainSelector: uint64 > amount: coins + > context: cell? > } */ export interface JettonLockBox_Deposited { @@ -4370,6 +4461,7 @@ export interface JettonLockBox_Deposited { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null } export const JettonLockBox_Deposited = { @@ -4380,6 +4472,7 @@ export const JettonLockBox_Deposited = { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null }): JettonLockBox_Deposited { return { $: 'JettonLockBox_Deposited', @@ -4395,6 +4488,7 @@ export const JettonLockBox_Deposited = { token: s.loadAddress(), remoteChainSelector: s.loadUintBig(64), amount: s.loadCoins(), + context: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_Deposited, b: c.Builder): void { @@ -4403,6 +4497,9 @@ export const JettonLockBox_Deposited = { b.storeAddress(self.token); b.storeUint(self.remoteChainSelector, 64); b.storeCoins(self.amount); + storeTolkNullable(self.context, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_Deposited): c.Cell { return makeCellFrom(self, JettonLockBox_Deposited.store); @@ -4415,6 +4512,7 @@ export const JettonLockBox_Deposited = { > token: address > amount: coins > recipientWallet: address + > context: cell? > } */ export interface JettonLockBox_WithdrawFailed { @@ -4423,6 +4521,7 @@ export interface JettonLockBox_WithdrawFailed { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null } export const JettonLockBox_WithdrawFailed = { @@ -4433,6 +4532,7 @@ export const JettonLockBox_WithdrawFailed = { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null }): JettonLockBox_WithdrawFailed { return { $: 'JettonLockBox_WithdrawFailed', @@ -4448,6 +4548,7 @@ export const JettonLockBox_WithdrawFailed = { token: s.loadAddress(), amount: s.loadCoins(), recipientWallet: s.loadAddress(), + context: s.loadBoolean() ? s.loadRef() : null, } }, store(self: JettonLockBox_WithdrawFailed, b: c.Builder): void { @@ -4456,6 +4557,9 @@ export const JettonLockBox_WithdrawFailed = { b.storeAddress(self.token); b.storeCoins(self.amount); b.storeAddress(self.recipientWallet); + storeTolkNullable(self.context, b, + (v,b) => b.storeRef(v) + ); }, toCell(self: JettonLockBox_WithdrawFailed): c.Cell { return makeCellFrom(self, JettonLockBox_WithdrawFailed.store); @@ -4719,7 +4823,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('te6ccgECyQEAMPQAART/APSkE/S88sgLAQIBYgIDAgLLBAUCASCfoAIBIAYHAgEgamsCASAICQIBICMkAgEgCgsCASAcHQL3T4kY6O0x8x1ywgfFP1LOMC8j/g7UTQ1PpI1NM/0SPQ1NTTB/QE9ATRgQCGbW1tkvALAG1tkvAKAG1tbW2S8AkAgQCHVhZWFlYWVhb4kviXBRETBQQREgQDEREDAhEQAhBfEE4QPRAsEFsQShA5ECgQVxBGEDUQJFYd8AyAwNAgEgGhsC/u1E0NT6SNTTP9EE0z8x+gAx+kgx+lAx9AX4koIAvj8l0NTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpI10z4KMjPhAL6UhL6UskByM+E0MzM+RbIz4oAQMv/z1ASxwXy9IIAvj8hbrPy9NDXLCJclUdE8r/XTCDQ+kgx1NQx0dCJ1ycODwPgVxJfDQSOKzw8AcMAkzY2NpkwODgQR14jEDTiAsjMzMsHE/QA9ADJyMz6UhLMyz/J7VTgXwkk1ywjaDv5dOMC1ywjBdcqtOMC1ywmqZO23J0wNALIzPpSzMs/ye1U4NcsIq2glczjAl8FhA8BxwDy9BESEwAI+n2kRAH+8r/TP9dM0NP/MddM0NM/MfpI+gAwJtDU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SNdM+CjIz4QC+lIS+lLJAcjPhNDMzPkWyM+KAEDL/89QbYsEyM+QPin6lhbLP1AD+gJSMPpSE/pU9ADPhCASzsnIz4UIEvpScc8LbszJcfsA0BAAkvpIMdTUMdHQ1ywn0+0iJPK/0z/UMdMfMfQB+lAwIG6RW44ayM+FCPpSghA0dupyzwuOyz/Pivj+yYBB+wDiAsjM+lLMyz/J7VQB/jUE0z/6SDHTPzH6ADH0BfiSggC+PlEVxwXy9IIAvj4hbrPy9NDXLCJclUdE8r/XTIQPIdD6SDHU1DHR0NcsJ9PtIiTyv9M/1DHTHzH0BDH6UDHRE7oS8vQj0NQx1DHTBzH0BDH0BDHR0PpI1NTRAdAB0AHXLCfT7SIk8r/TP9QUAvw1BNM/+kgx+gAx+kgx9AX4koIAvj5RFccF8vSCAL4+IW6z8vTQ1ywlaqO2dPK/10yEDyHQ+kgx1NQx0dDXLCGo+78c8r/TP9Qx0x8x+lAx0RO6EvL00PpIMdTUMdHQ1ywhqPu/HPK/0z/THzH6UDAgbpFb4w4CyMz6UszLP8kVFgL+NQTTP9RvAAHTB/QEkyFus44SAdD0BJr6SFBVb4wkxwAV5jAB6DEib4hYuvKJ10z4kvgoBcjLPxX6UiPPFCJviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTcG+BWPpSAaTkAckCoeQwMTTPCwcS9ADJUkCBOj4CyM+E0MzM+RbIiRcYAf7THzH0AfpQMAHQ0/8x1NHQ1DHTP/pIMfoAMfpI0QT6ADH6ANTXTAbI+lIX+lIh+gLJyM+PGAAEghA33W9uzwv3cM8LYRPLPxLMyXD7ACFukl8FjiEEyMwTzMnIz4WIE/pSghD0MqTjzwuOyz/MAfoCyYBA+wDiAsjM+lLMyz/JFgA0yM+FCPpSghDvDLNuzwuOyz/PiOkWyYBB+wAABO1UAAOAEAH8zxbL/89QFMcFE/L0AdD6SDH6ADH0BDH6ADHTPzHTHzHU0dDXLCapk7bc8r/TP9EB0NcsJWqjtnTyv9TR0PpIMdTU0YQPItDXLCGo+78c8r/TP9Qx0x8x+lAx0RS6E/L00NcsIaj7vxzyv9M/1NMfMfpQMAHQ0/8x10zQA9ADGQDq+kjTP9P/MfpIMAXTHzH6ADH6ADD4KAPI+lLJBsj6UhP6UiL6AhXMycjPjxgABIIQ6cAMl88L93DPC2EVyz8UzMlw+wAgbpJfA44eyFAD+gLJyM+FCBP6UoIQ4OiC9c8Ljss/zMmAQfsA4gLIzPpSzMs/ye1UAFcIW6SW3DggmkAAAAAAAAAAAAAAAAAAAEigwb0Dm+hMZJbf+ABgwb0Dm+hMYABpCDCTZMwcHHgcXqTIsIAjiAicbDAAZyE/yKpBCG+8oRmqAHeAqsAIMIAlFEgqALeAugwMXCACASAeHwIBICEiAak7aLt+9csJ5Db7QyORNcsJ88U8lSUW3DbMeGCAMKKI26z8vQhggDCigTHBRPy9CBtA9cLP4sCAcjLPxX6UhL6UsnIz4cgFM5xzwthE8zJcPsA4w1/gIACPDAjs5F/lSDAAMMA4pEw4FNS+CMnoRBoXjQQN0iAUoDwBzVRZbmVgWa98vDgUzS5jhBfBSCVgWa+8vDhMIFmvvLw4FA0oVA0gAGZsEtM/+kgwggDCiFE0xwUT8vSCAMKJUyPHBbPy9CGLAsjPhyDOcM8LYRLLPxL6Uslw+wAAWQwI7ORf5UgwADDAOKRMOBTUvgjJ6EQaF40EDdIgFKA8Ac1UEWgUAW2CEREA4AA1CCOFTZcvJWBZrzy8OBRUqigFLYI+CNQROBbgAgEgJSYCASAvMAIBICcoAgEgKywAKQhkVvhgTpJIZQCusMAk2whcOLy9IAL1GwzMzMi0CLQAsj6UhTMEszJJtDU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SNdM+CgiyM+EAhL6UvpSyQHIz4TQzMz5FsjPigBAy//PUATXLCfT7SIk8r/TP9dM0NP/MddM0AT6ADH6ADAE1ws/yM+RLkqjoiTPFMlTJciJgKSoACJ6ew2EAvs8WEss/FfpSEss/UAP6AhL0AMnIz4PMz1CCEAX14QD4KMjPkS5Ko6IVzMnIz5A+KfqWFMs/UAX6AlKA+lIT+lT0AM+EIM7JyM+FiBP6UgH6As+Bc/oCcc8LZczJcfsAAfcgTpFCcMAGfL0ggC+PCmEP73y9IIAvj2LAizHBbPy9CvQ1NQx0wcx9AQx9AQx0dDUMfpIMdQx+kjXTCbQ0/8x10zQ+kjXCz9TE8jPhAIS+lL6UslQA8jPhNDMzPkWyM+KAEDL/89QyM+Q1H3fjirPCz8ZzBfLHxX6VMkDgLQAZF8EggC+PCGEP73y9IAH+yMsfIvoCUAn6AskHyPpSEswWzMmEDyiEP73y9Cek+CjIz5K1Uds6I88UyQZvAQrIyz/6UhXMKG+Ic21UciGpBo4bAcj0AFMhtghRIqEimVPQb4FY+lIBpOQByQKh5DAxOs8LBxj0AMkoggkxLQCLCMjOycjPiQgBU0PIz4TQzC4A+Mz5Fs8L/1j6AoEAjc8LcCLPFCPPFMzJcfsAyM+E0MzM+RbIz4oAQMv/z1DIz5K1Uds6GMzJB8j6VBf0AMmCEAX14QDIz5NBlwwaFcs/FvpSFss/UAP6AhT6UhL0AMnIz4WIUlD6UlAD+gLPgXP6AnHPC2USzMlx+wCBAIYCASAxMgIBIF9gAT07aLt+yDXLCfT7SIknmwh0z/U0x/0BPpQMPAN4w5/gMwHxCbDAJUrbrPDAJFw4o4jVxMJERYJCBEVCAcRFAcGERMGVQQREirapAMREANP7YEAhg3gERYRHBEWERURGxEVERQRGhEUERMRGRETERIRGBESERERFxERERARHBEQDxEbDw4RGg4NERkNDBEYDAsRFwsKERwKCREbCYFwB/tcsIEeX/byOdGwx0z8x10zQ+kjU1NEB0AHQAdcsJ9PtIiTyv9M/1NMf9AT6UDAF+gD6ANTXTMjPk+n2kRIozws/F8wVyx8T9ABSYPpUychY+gJY+gISzBLMycjPk56NQQYTyz8U+lITzBLMycjPhYgS+lJxzwtuzMmAQPsA4w40A/rXLCU2/TEcj3JsMdM/MddM0PpIMdTU0QHQAdAB1ywn0+0iJPK/0z/U0x/0AfpQMALQ0/8x1NHQ1DHTP/pIMfoAMfpIMdEE+gAx+gAwAeMPVhVukzBXFI4iyM+FCAERFgH6UoIQNHbqcs8LjgERFQHLP8+I6RbJgED7AOLjDjU2NwL8gTo4JFYZgED0Dm+hEvL01PQE1NTRINDU1NEB0NN30z/SANN303fRIo44XwYB0NTU0QHQ03fTP9IA03fTd9FWI9DUMfpIMdQx+kgwUqDwBgTIy3cTyz/KAMt3y3fJyMzMyQHjDQPIzBL0AMzMUkIRGYBA9ENWGtDUMfpIMdQxODkB9oE6OCRWGYBA9A5voRLy9NT0BNTU0QHQ1NTRAdDTd9M/0gDTd9N30VYj0NQx+kgx1DH6SDBSoPAGBMjLdxPLP8oAy3fLd8nIzMzJA8jMEvQAEszMUkIRGYBA9ENWGtDUMfpIMdQx+kgwBMjLPxT6UgERF/oCycjPjxgABDoC/NcsI5sWhOSOEjHTP/oA+lBVA/GAEoAegBfbOI9gbBLXLCGo+78cmzHTP9TTH/pQMPAVj0jXLCTxU1s0jjNsIdM/MddM0PpI1NTRAdAB0AHXLCGo+78c8r/TP9TTH/pQMATTH/oA+gAwEFYQRRA08BuPCdcsIQ847DzjD+Li4js8AFY2ViPQ1DH6SDHUMfpIMBBFEDRBMFQmoPAGBMjLdxPLP8oAy3fLd8nIzMzJAFD6SDAEyMs/FPpSAREX+gLJyM+PGAAEghAUH34szwv3cc8LYczJcPsAACSCEDDrq9vPC/dxzwthzMlw+wAC8Gwh0z8x10zQ+kgx1NTRAdAB0AHXLCGo+78c8r/TP9TTH/pQMALQ0/8x1NQx9AQx9AQx0dD6SDHTP9Qx0/8x+kgx0QTTHzH6ADAB4w9WFW6TMFcUjiLIz4UIAREWAfpSghDvDLNuzwuOAREVAcs/z4jpFsmAQPsA4j0+AyjXLCK3uem8jwnXLCC+Ehbk4w/jDUJDRAL+gTo4JFYZgED0Dm+hEvL01PQE1NTRINDU1NHQ03fTP9IA03fTd9EijjhfBgHQ1NTR0NN30z/SANN303fRViPQ1DH6SDHUMfpIMFKg8AYEyMt3E8s/ygDLd8t3yQHIzMzJAeMNA8jMEvQAzMxSQhEZgED0Q1Ya0NQx+kgx1DH6SD9AAfaBOjgkVhmAQPQOb6ES8vTU9ATU1NEB0NTU0dDTd9M/0gDTd9N30VYj0NQx+kgx1DH6SDBSoPAGBMjLdxPLP8oAy3fLd8kByMzMyQPIzBL0ABLMzFJCERmAQPRDVhrQ1DH6SDHUMfpIMATIyz8U+lIBERf6AsnIz48YAARBAFg2ViPQ1DH6SDHUMfpIMBBFEDRBMFQmoPAGBMjLdxPLP8oAy3fLd8kByMzMyQBMMATIyz8U+lIBERf6AsnIz48YAASCEDR//HzPC/dxzwthzMlw+wAAJIIQdAmtj88L93HPC2HMyXD7AAH8MdM/0z/XTFYa0NT6SDHUMfpIMdQx0x8x+lAx0dD6SPpQMdEkggDCiALHBfL0gTo4IlYZgED0Dm+hMfL0gTo4IlYZgED0Dm+hEvL01PQE1NTRgTo3JdDTByHBQfKFAaoC1xjR10nDAPL0JPkAgTo/UxSDB/QOb6Exs/L0VEUURQPy1ywiE1xmJI9u1ywmu4lAhI7j1ywh4oUc3I5YMdM/1wsfERnQ1PpI1PpI1NMfMfpQ0SXQ+kj6UDHRKIIAwogCxwXy9FYeBsjMFfpSE8z6UswSyx/6VMnIz4UIE/pSghBCanE7zwuOyz8BERgByx/JgED7AOMO4w3jDUZHSAL+MdM/1NdMVhrQ1PpIMdQx+kgx1DHTHzH6UDHR0PpI+lAx0SSCAMKIAscF8vQB0JQgxwCzjj8g10sBkTCbgTS8AcAB8vTXTNDi0z9SEBEZgED0W4E6OAHy9MjPjxgABIIQJ5CCi88L93DPC2ESyz/JcPsAERfoMNCUIMcAs4roMFdYAKSDB/QXyM+PGAAEghC/DRq2zwv3cM8LYSbPCz8lzxTJcPsAA8jME/QAEszMUiIRGYBA9EPIz4UIFPpSghASzEmFzwuOEss/yz8BERUBzMmAQPsAA/TXLCH65Pq8j2/XLCJ/FpNkjuQx0z/XTFYZ0NT6SDHU+kgx1DHTHzH6UDHR0PpI+lD6UNED0PpI+lAx0ZLwHABUJHDsTySBOj4DxwWSMH+U2gHDAOLy9NCUIMcAs4roMMjPhQgS+lKCEN17DHHPC47LP8mAQPsA4w7jDUlKSwDmMdM/+kj6UPpQMBEb0NT6SNQx+kjU0x/6UNEl0PpI+lAx0SqCAMKIAscF8vQnyPpSUnD6VFYhAfpUyQbIzBX6UhXMEvpSzBLLH/pUycjPktzXjDIUyz8S+lL6VAERGQH6VMnIz4UIEvpScc8LbszJgED7AAH6MdM/0z/XTFYa0NT6SDHUMfpIMdQx0x8x+lAx0dD6SPpQMdEkggDCiALHBfL0gTo4IlYZgED0Dm+hMfL0gTo4IlYZgED0Dm+hEvL01PQE1NTRJPkAUAODB/RbgTpAAfL0A8jME/QAEszMUiIRGYBA9EPIz48YAASCELwUx+hWAvwg10sBkTCbgTS8AcAB8vTXTNDi0z/SANTUgTo4JVYcgED0Dm+hEvL01PQE1NTRJ44/AdDUMdQx0SXQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kl0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAcjMzMkB4w0DyMwS9ADMzFJSERxMTQPy1ywhhQ6PvI9u1ywnGDsl9I7j1ywkyU2yFI5YMdM/+kgwERnQ1PpIMdT6SNTTH/pQ0SXQ+kj6UDHRKIIAwogCxwXy9FYeBsjMFvpSFMwS+lLMyx/6VMnIz4UIE/pSghDl0IsuzwuOyz8BERgB+lLJgED7AOMO4w3jDU5PUACuMdM/+lAwERnQ1PpI1PpI1NMf+lAx0SXQ+kj6UDHRKIIAwogCxwXy9FYeBsjMFfpSE8z6UszLH/pUycjPhQgT+lKCEDyGnYDPC47LPwERGAH6VMmAQPsAAHrQ1DHUMdEl0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJJdDSANN303fR+CMiyMt3yz8TygDLd8t3yQHIzMzJAEqAQPRDyM+PGAAEghD/nb92zwv3cM8LYRXLPxPKAMzMyXD7ABEWAf7XLCTtJtBMjk8wVhjQ1PpI1PpI1NMf+lDRBtD6SPpQ0UEJKPAEjig3Vx4RHcj6UhX6VMnIzBL6UswS+lIBERkBzMsfAREXAfpUyREWf9sx4BB4XwjHANsx4THTP/QFVhnQ1PpI1DH6SDHUMdMfMfpQMdEB0PpI+lAx0SSBOj4CUQHQMdM/10xWGdDU+kgx1DH6SDHUMdMfMfpQMdHQ+kj6UDHRI4IAwogCxwXy9BEY0PQE9AT0BNERGtCUIMcAs4roMAHI9AD0AAERGAH0AMnIz4UIEvpSghDX9cVjzwuOAREXAcs/yYBA+wBSAv4x0z/U10xWGtDU+kgx1DH6SDHUMdMfMfpQMdHQ+kj6UDHRJIIAwogCxwXy9AHQlCDHALOK6DDQlCDHALOOOiDXSwGRMJuBNLwBwAHy9NdM0OLTP1IQEReAQPRbMMjPjxgABIIQ1kbH0c8L93DPC2ESyz/JcPsAERXoMMjPhQgSU1QAhMcFkjF/llJCxwXDAOLy9BEY0PQE9AT0BDHRVhkCyPQA9AD0AMnIz4UIE/pSghAVgAFhzwuOyz8BERcB9ADJgED7AADUINdLAZEwm4E0vAHAAfL010zQ4tM/+lD6UCJul1I2gED0WzCbIsj6UlQgR4BA9EPiIW6XUjWAQPRbMJshyPpSVCBGgED0Q+IDyMs/EvpU+lTJyM+PGAAEghCcWruVzwv3cc8LYczJcPsAWAH+INdLAZEwm4E0vAHAAfL010zQ4tM/0x/TH/oA+gDTD9MP0gCBOjgpViGAQPQOb6Ex8vSBOjUi8vSBOjQkgScQufL0gTo0I4EnELny9IE6NSjCAPL0J8jLHyfPCx8m+gIl+gIkzwsPI88LDyLPCgBSkhEggED0QwfIyx8Wyx9QBFUAJPpSghAoy8xkzwuOyz/JgED7AABU+gJY+gLLD8sPygDJyM+PGAAEghD75h8Vzwv3cM8LYRPLPxLMyXD7ABEWAGTPC/dwzwthIs8LP1YYzxTJcPsAyM+FCBT6UoIQ4XvzzM8LjhLLP8s/AREVAczJgED7AAH+INdLAZEwm4E0vAHAAfL010zQ4tM/1NTUgTo3I9DTByHBQfKFAaoC1xjR10nDAPL0gTo7JVYcgED0Dm+hMbPy9AHQ1NTRbQLQ0gDTd9N30fgjIsjLd8s/E8oAy3fLd8kB0NIA03fTd9H4IyLIy3fLPxPKAMt3y3fJAcjMzMn4I1kALsjPhQgS+lKCEK14M9fPC47LP8mAQPsAAbRwyMt3yz9wzwvwyfgjcMjLd8s/cM8L8MkByMzMySQG0JQgxwCziugwBcjMEvQAzBPMUjIRGoBA9EPIz48YAASCEO03xLzPC/dwzwthE8s/AREYAczJcPsAERZaAf4g10sBkTCbgTS8AcAB8vTXTNDi0wchwUHyhQGqAtcYyCLXSSCpOALyRasCIMFB8oXPCwcSzsmBOjch0NMHIcFB8oUBqgLXGNHXScMA8vQg+QCBOj9TFoMH9A5voTGz8vRUQRaDB/QXyM+PGAAEghC/DRq2zwv3cM8LYSnPCz8VWwAKzMlw+wAB+ggRGggHERkHBhEYBgURFwUEERwEAxEbAwIRGgIBERkBERhWF1YdVh1WHVYdVh3wDgSOOF8EVxdXF1cXVxdXF1cXERARFhEQDxEVDw4RFA4NERMNDBESDAsREQsKERAKEJ8QjhB9EGxVVVUE4MjPk+n2kRJWIc8LPwERIAHMXQH8AREeAcsfAREcAfQAVhoB+lTJyAERHPoCAREc+gIBERwBzAERGgHMycjPk56NQQYBERwByz8BERYB+lIBERcBzAERGQHMycjPhYgBERUB+lJxzwtuAREUAczJgED7ABEQERYREA8RFQ8OERQODRETDQwREgwLERELChEQChCfXgAkEI4QfRBsEFsQShA5SBZFVQcDAfUJsMAlSpus8MAkXDijjFXEwkRFgkIERUIBxEUBwYREwZVBBESVhbaqQgRFQgHERQHBhETBgUREgWBAIYRElVA4CPQ0//U0SDQ0z/6SDH6APpIMIE6PVYi0NQx+kgx1DH6SDBYxwXy9IE6OSJWH4BA9A5voTHy9FYgViCBhAGsUM1fCzg4ODkFwwCVI26zwwCRcOKYNgVQREYW2mDgXwUygTo6AdD0BDH0BDH0BNFY8AKz8vSAB/FYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYgViBWIFYY8A9WH9D0BPQEMfQEMdFSIIBA9A5voZP6SNGSMG3igTo+IW6z8vSBOj5RG8cF8vQqwwCWVhZus8MAkXDijhBWG1YbVhtWG1YbU+ZWHdpw3lYgAmID/lYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAlYgAgERGgERGVYd8BBRIqEl4w+BOjhRIYBA9A5voRPy9AHU9AQx1DHUMdFWHcjL/8nIz5Pp9pESKc8LPyjPFCdjZGUC/lYf0NQx+kgx1DH6SDHUMdMf+lAx0VJg8AiBOjgiVh6AQPQOb6ES8vTU9ATU1NEg0NTU0QHQ03fTP9IA03fTd9EijjhfBgHQ1NTRAdDTd9M/0gDTd9N30VYo0NQx+kgx1DH6SDBSoPAFBMjLdxPLP8oAy3fLd8nIzMzJAeMNA8hmZwH+gTo4IlYegED0Dm+hEvL01PQE1NTRAdDU1NEB0NN30z/SANN303fRVijQ1DH6SDHUMfpIMFKg8AUEyMt3E8s/ygDLd8t3ycjMzMkDyMwS9AASzMxSIhEegED0Q1Yf0NQx+kgx1DH6SDAiyMs/+lJWHfoCycjPjxgABIIQz1BZ/GgB/s8LH1Jg9ABSUPpUycgl+gJWH/oCI88UIs8UyStWIwlWIwlWI1GYViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJViMJCBEjCAcRIgcGESEGBREgBQIRIAIBESIBESHwEQMRGANpAFY2VijQ1DH6SDHUMfpIMBBFEDRBMFQmoPAFBMjLdxPLP8oAy3fLd8nIzMzJAHzMEvQAzMxSIhEegED0Q1Yf0NQx+kgx1DH6SDAiyMs/+lJWHfoCycjPjxgABIIQ66SMC88L93HPC2HMyXD7AAAYzwv3cc8LYczJcPsAAARDNAIBIGxtAgEgkJECASBubwIBIH5/AgEgcHECASBzdACfFcSVxBfDzU1WzMzAtDTP/pIMfoAMAKAQPQOb6GTXwNw4dMfMdMfMfoAMfoAMdMP0w/SANGTXwRw4QORMJEy4oE6NCKBJxC58vSogScQqQSABoQ4OTk6Ojo6Ojo6Ojo/Pz8/A8MAlSpus8MAkXDijhI+EI0QfBBrEFoQSVAHBgUE2tHgbFU1NQfQ1DH6SDHUMfpIMdQx0x8x+lDRIG7jA18IcIHIAliTQ+gAx+gDUMdQx0fgoBMj6UhPMFczJyM+RBKdEJhTLPxfMFcsfE/QAUAT6AhL6UvQAycjPhYgS+lLPhBBz+gJxzwtlzMmAQPsAfwH1CbDAJUobrPDAJFw4o4lMFcSCBEVCAcRFAcGERMGBRESBVUDERFWE9qUAxEQA0/tgQCGDeCBOj5WHdDUMfpIMdQx+kjXTPgoyM+EAvpSEvpSyQHIz4TQzMz5FsjPigBAy//PUCfHBfL0VhxWHFYcVhxWHFYcVhxWHFYcgdQL3CjDAJUpbrPDAJFw4o4kVxULERgLChEXCgkRFgkIERUIVQYRFFYV2sQDERADT+2BAIYN4GwzMzMC0AHQAdcsJ9PtIiTyv9M/1NMfMfQB+lAwAdDT/zHU0dDUMdM/+kgx+gAx+kjRBPoAMfoA1NdMBsj6Uhf6UiH6AsnIiYHx9Av5WHFYcVhxWHFYcVhxWHFYcVhxWHFYcVhxWHFYcVhxWHFYcVhxWHI5MW1YbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbBVYbRRVQRAPwFO3juoAcf+0Riu1BdncD/CH0BCFumDEgxwCSMG3gktHQ4vpI1NTRVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BVh8BER9WHYrt47qAHn/tEYrtQe3xAXh5egAK7fEB8v8B/jFWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgVWHgUEER4EAxEdAxEcWfAU+CdvEFMBvJGikltw4vgvoHL7AgHQ1ywn0+0iJPK/0z/UMdMfMfQB+lAwIG57AGAg0NcsJ9PtIiTyv9dM0NP/MddM0NM/MfpIMfoAMIE6SFEXuvL0gTpHJW6z8vRY8BMABPL/AD6SXwOOGcjPhQj6UoIQNHbqcs8Ljss/yw/JgQCC+wDiAAXGAAEAgs8WghA33W9uzwv3cM8LYRPLPxLMyXD7ACFukl8FjiEEyMwTzMnIz4WIE/pSghD0MqTjzwuOyz/MAfoCyYBA+wDiAgEggIECASCEhQD3DBsxGyENDSBOj4F0NQx+kgx1DH6SNdM+CjIz4QC+lIS+lLJAcjPhNDMzPkWyM+KAEDL/89QIscFFfL0IW6SXwTgghAF9eEAbYsEyM+QPin6lhfLP1AF+gJSMPpSE/pUE/QAz4QgE87JyM+FCBL6Ulj6AnHPC2rMyXP7AIAHxCXDAJUubrPDAJFw4o4vVxIDERUDAhEUAgEREwERElYUVhRWFFYUVhHahAMRFQMCERQCARETARESgQCGERLeERYRGxEWERURGhEVERQRGREUERMRGBETERIRFxESERERGxERERARGhEQDxEZDw4RGA4NERcNDBEbDIIIB/AsRGgsKERkKCREYCQgRFwgHERsHBhEaBgURGQUEERgEAxEXAwIRGwIBERoBERlWGFYYVh1WHVYd8BYDjjRfA1cXVxdXF1cXVxcREREWEREREBEVERAPERQPDhETDg0REg0MEREMCxEQCxCvEJ4QjVVH4BEZER4RGREYER0RGIMArhEXERwRFxEWERsRFhEVERoRFREUERkRFBETERgRExESERcREhERERYREREQERUREA8RFA8OERMODRESDQwREQwLERALEK8QnhCNEHwQaxBaEEkQOFjwGwH3CXDAJUtbrPDAJFw4o4wVxIIERUIBxEUBwYREwYFERIFVQMRESzamAcRFAcGERMGBRESBQQREQSBAIYREVUw4CLQ0/8x1NT0BPQEMdEC0PpIMdM/1DHT//pI0YE6PVYh0NQx+kgx1DH6SDBYxwXy9IE6OSJWHoBA9A5voYIYAUxXElcQXw81NVtsE4BA9A5voZJbcOHUMfQE1DHUMdEB+QABgwf0Dm+hMYAL+MfL0Vh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YY8A9WHtD0BDH0BPQEMdFSIIBA9A5voZP6SNGSMG3igTo+IW6z8vSBOj5RGscF8vQpwwCWVhRus8MAkXDi4wBWH1YfVh9WH1YfVh9WH4eIACBWGlYaVhpWGlYaU9ZWG9pwAf5WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YfVh9WH1YYgTpAERvwFxPy9FYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAVYeAREZ8BiS8BkAElYfAlYfiQP+AlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfAlYfWYAZf+0R2gEgwQCRf5YghHe8wwDilYE6QvLw4CPjDyJWGsjPkNR9344nzws/Js8UJc8LH1JA+lTJIsjLHyL6AoqLjAL+Vh3Q1DH6SDHUMfpIMdQx0x/6UDHRUkDwCIE6OCJWHIBA9A5voRLy9NT0BNTU0SDQ1NTR0NN30z/SANN303fRIo44XwYB0NTU0dDTd9M/0gDTd9N30VYm0NQx+kgx1DH6SDBSoPAFBMjLdxPLP8oAy3fLd8kByMzMyQHjDQPIzI2OAfSBOjgiVhyAQPQOb6ES8vTU9ATU1NEB0NTU0dDTd9M/0gDTd9N30VYm0NQx+kgx1DH6SDBSoPAFBMjLdxPLP8oAy3fLd8kByMzMyQPIzBL0ABLMzFIiERyAQPRDVh3Q1DH6SDHUMfpIMALIyz8S+lJWGvoCycjPjxgABI8A1FYd+gLJKVYhCFYhCFYhUYAIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIViEIBxEhBwYRIAYFER8FBBEgBAMRHwMCESEC8BoCERcCQTMAWDZWJtDUMfpIMdQx+kgwEEUQNEEwVCag8AUEyMt3E8s/ygDLd8t3yQHIzMzJAHwS9ADMzFIiERyAQPRDVh3Q1DH6SDHUMfpIMALIyz8S+lJWGvoCycjPjxgABIIQy8QOW88L93HPC2HMyXD7AAAkghCLsl+ozwv3cc8LYczJcPsAAgEgkpMCASCamwIBIJSVAgEglpcAYxXEF8PNl8EMiBukTDgMdCBOkEh10mDB7qXIddKwADDAJFw4vL00//RgTpBIYQHu/L0gAIEVxBfDzZfBDJTIbqSbCHgUyG8nQKi8AOVgTpC8vDgqQTgAqHwA5WBOkLy8OCBOkIhmYT/IqkEI77DAJF/4vL0qIAGXDc4ODg4ODg5OTk5OT4+Pj4BwwCVLG6zwwCRcOKfPRB8EGsQWhBJEDhVFdrB4GxjNgbQ1DH6SDHUMfpIMdQx0x8x+lDRIG7jA18HcIJgB9wowwCVLm6zwwCRcOKOIQwRGQwLERgLChEXCgkRFgkIERUILtrVBBERBAMREANP7eAyMwPQ0/8x1NQx9AQx9AQx0dD6SNM/1DHT/zH6SNECyPpSyQLI+lIX+lJQA/oCEszJyM+PGAAEghDpwAyXzwv3cM8LYRXLPxTMyXCCZAJAl0NMfMfoA+gAx0fgoA8j6UhjMFszJyM+RwPCtYhXLPxPMUAX6AhTLHxP6UhL0AMnIz4WIEvpSz4QQc/oCcc8LZczJgED7AH8ATvsAIm6SXwOOHcgB+gLJyM+FiBP6UoIQ4OiC9c8Ljss/zMmAQPsA4gIBIJydAK9BNfA1cSVxBfDzU1WzMD0NQx+kgx1DH6SDHUMdMf+lAx0VIQ8AhZgED0Dm+hlltwVHAAcOHTH9Mf+gD6ANMP0w/SANGXXwdwVHAAcOEGlDAxA3/gMTQDf4AB0MTIgbrOUxwXDAJJbcOKAB9xXElcQXw81NVuBOjg0gED0Dm+hE/L0AdQx9AQx1NTRAo5XMNDU1NEB0NN30z/SANN303fR+CNQBKEjqBSgI7YI+CMByMt3yz/KABLLd8t3yQHQ03fTP9IA03fTd9H4I1AEoSOoFKAjtgj4IwHIy3fLP8oAEst3y3fJ4DGCeAKzQ1NTRAdDTd9M/0gDTd9N30fgjUAShI6gUoCO2CPgjAcjLd8s/ygASy3fLd8kB0NN30z/SANN303fR+CNQBKEjqBSgI7YI+CMByMt3yz/KABLLd8t3yQIBIKGiAgEgvb4CASCjpAIBILGyAgEgpaYCASCvsAIBIKeoAgEgra4CAWapqgIBSKusAF+gX7UTQ1PpIMdQx0z8x0dDU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SDHUMdMf+lAx0YAdaHHtRNDU+kgx1DHTPzHRINAx1DHUMdMHMfQE9AQx0W0hgED0hm+lMpEBnVICbwJREoBA9HxvpTLoMDGAGmlddqJoan0kGOoY6Z+Y6OhqGOppg5j6Ahj6Ahjo6HoCGPoCegIY6MAgegc30Mn9JGjJGDbxQBPpyPaiaGp9JBjqGOmfmOjoamoY6YOY+gIY+gIY6OhqGP0kGOoY/SQYQB3rK/Ghe2NLc1lzG0MLS3Fzo3txcxsbS4FyY3sbWpMrYysLmypjextbE3vCo3tbK3KDe3tkEWpgXGJcYRAAMWt2HaiaGp9JGppn+iR6GpqaYP6AnoCaMCAQza2tsl4BYA2tsl4BQA2tra2yXgEgAiIiIwIiIiICIuIiAeIiweHCIqHBoiKBocIiYcGiIkGhwiIhwaIiAaIZ6qVwIBDqpB4CEAAO7NqO1E0NT6SDHUMdM/MdHQ1DHUMdMH9AQx9AQx0YAC7sB87UTQ1PpI1NM/0SPQ1NTTB/QE9ATRgQCGbW1tkvALAG1tkvAKAG1tbW2S8AkAERERFxERERARFhEQDxEVDw4RFA4NERMNDxESDw4REQ4NERANEM5VG4EAhwLwF4AIBILO0AgEgu7wCASC1tgBlsnG7UTQ1PpIMdQx0z8x0SDQMdTUMdMHMfQEMfQEMdHQ1DH6SDHUMfpIMdQx0x8x+lDRgAgEgt7gCAWK5ugD4qoftRNDU+kjU0z/RI9DU1NMH9AT0BNGBAIZtbW2S8AsAbW2S8AoAbW1tbZLwCQAREREbEREREBEaERAPERkPDhEYDg0RFw0RFREWERURFBEVERQRExEUERMREhETERIMERIMCxERCwoREAoQnxCOEH0QbFVVgQCHVVDwHgBuqR3tRNDU+kgx1DHTPzHR0NTUMdMHMfQEMfQEMdHQ1PpIMdQx+kgx1DHTHzH6UDHR0PpI+lAx0QBXofu1E0NT6SDHUMdM/MdEg0DHU1DHTBzH0BDH0BDHR0NQx+kgx1DH6SDDHBYAV6B/tRNDU+kgx1DHTPzHRINAx1NQx0wcx9AQx9AQx0dDUMddM0PpI+lD6UNGALuyXrtRNDU+kjU0z/RI9DU1NMH9AT0BNGBAIZtbW2S8AsAbW2S8AoAbW1tbZLwCQAREREXEREREBEWERAPERUPDhEUDg0REw0PERIPDhERDg0REA0QzlUbgQCHAvAdgAGuy4btRNDU+kgx1DHTPzHR0NQx1NMHMfQEMfQEMdHQ9AT0BDH0BDHRgED0Dm+hk/pI0ZIwbeKACASC/wAIBIMHCAGG0fb2omhqfSQY6hjpn5jo6GpqGOmDmPoCGPoCGOjoahj9JGoY/SQY6hjpj5j9KBjowAIm33P2omhqfSQY6hjpn5jokGgY6hjqGOmDmPoCGPoCaMAgegc30McJaY/pj/0AfQBph+mH6QBowIBEzJg2tra2tra2uHFACASDDxAIBIMXGAKWxG3tRNDU+kgx1DHTPzHRINAx1DHUMdMHMfQE9AQx0YE6OFmAQPQOb6ES8vTUMfQE1DHUMdFtIYMH9IZvpZCeAdTRWG8CURKDB/R8b6XoECNfA4AAfsop7UTQ1DH6SNQx0z8x0YAIBasfIAEmyHHtRNDU+kgx1DHTPzHR0NQx1DHTBzH0BPQEMdGAQPQOb6ExgAGmlX9qJoan0kGOoY6Z+Y6JBoGOoY6hjpg5j6AnoCGOjAnRwswCB6BzfQiXl6anoCGOoY6hjowBVpwPaiaGp9JBjqGOmfmOjoahjqaYOY+gIY+gIY6Oh6Ahj6Ahj6AmiA+AFZw=='); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4744,11 +4848,10 @@ export class LockReleaseLockboxTokenPool implements c.Contract { 'RateLimiter_Error.BucketOverfilled': 26300, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, 'RateLimiter_Error.TokenRateLimitReached': 26302, - 'LockReleaseLockBoxTokenPool_Error.PendingLockAlreadyExists': 48700, - 'LockReleaseLockBoxTokenPool_Error.PendingLockNotFound': 48701, - 'LockReleaseLockBoxTokenPool_Error.PendingReleaseAlreadyExists': 48702, - 'LockReleaseLockBoxTokenPool_Error.LockboxNotConfigured': 48704, - 'LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender': 48705, + 'LockReleaseLockBoxTokenPool_Error.ContextExecutorUnavailable': 48700, + 'LockReleaseLockBoxTokenPool_Error.LockboxNotConfigured': 48701, + 'LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender': 48702, + 'LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce': 48703, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, @@ -4780,8 +4883,8 @@ export class LockReleaseLockboxTokenPool implements c.Contract { static fromStorage(emptyStorage: { poolData: TokenPool_Data lockbox: c.Address - pendingLocks: Map /* = [] as map> */ - pendingReleases: Map /* = [] as map> */ + contextExecutorCode: c.Cell + contextExecutorNextId: uint64 }, deployedOptions?: DeployedAddrOptions) { const initialState = { code: deployedOptions?.overrideContractCode ?? LockReleaseLockboxTokenPool.CodeCell, @@ -4941,6 +5044,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null }) { return JettonLockBox_Deposited.toCell(JettonLockBox_Deposited.create(body)); } @@ -4950,10 +5054,28 @@ export class LockReleaseLockboxTokenPool implements c.Contract { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null }) { return JettonLockBox_WithdrawFailed.toCell(JettonLockBox_WithdrawFailed.create(body)); } + static createCellOfContextExecutorForwardNotificationLockReleaseLockboxTokenPoolReleaseContext_(body: { + id: uint64 + context: LockReleaseLockboxTokenPool_ReleaseContext + forwardFrom: array + message: ContextExecutor_InMessageForward + }) { + return makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseLockboxTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ); + } + async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5188,6 +5310,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { token: c.Address remoteChainSelector: uint64 amount: coins + context: c.Cell | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5201,6 +5324,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { token: c.Address amount: coins recipientWallet: c.Address + context: c.Cell | null }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -5209,6 +5333,27 @@ export class LockReleaseLockboxTokenPool implements c.Contract { }); } + async sendContextExecutorForwardNotificationLockReleaseLockboxTokenPoolReleaseContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + id: uint64 + context: LockReleaseLockboxTokenPool_ReleaseContext + forwardFrom: array + message: ContextExecutor_InMessageForward + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseLockboxTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ), + ...extraOptions + }); + } + async getTypeAndVersion(provider: ContractProvider): Promise<[ c.Slice, c.Slice, @@ -5260,20 +5405,6 @@ export class LockReleaseLockboxTokenPool implements c.Contract { ); } - async getHasPendingLock(provider: ContractProvider, queryId: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('hasPendingLock', [ - { type: 'int', value: queryId }, - ])); - return r.readBoolean(); - } - - async getHasPendingRelease(provider: ContractProvider, queryId: uint64): Promise { - const r = StackReader.fromGetMethod(1, await provider.get('hasPendingRelease', [ - { type: 'int', value: queryId }, - ])); - return r.readBoolean(); - } - async getRMNProxy(provider: ContractProvider): Promise { const r = StackReader.fromGetMethod(1, await provider.get('getRMNProxy', [])); return r.readSlice().loadAddress(); From 983976ada864a18731b1c9736f4d8922110c1606 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Fri, 31 Jul 2026 10:20:47 +0200 Subject: [PATCH 05/11] yarn fmt --- contracts/tests/ccip/ContextExecutor.spec.ts | 2 +- .../ccip/pools/BurnMintTokenPool.spec.ts | 6 +- .../pools/LockReleaseLockboxTokenPool.spec.ts | 37 ++++++------ .../ccip/pools/LockReleaseTokenPool.spec.ts | 58 +++++++++---------- .../tests/ccip/pools/TokenPool.behavior.ts | 5 +- 5 files changed, 53 insertions(+), 55 deletions(-) diff --git a/contracts/tests/ccip/ContextExecutor.spec.ts b/contracts/tests/ccip/ContextExecutor.spec.ts index 287f3af64..944b5c3ae 100644 --- a/contracts/tests/ccip/ContextExecutor.spec.ts +++ b/contracts/tests/ccip/ContextExecutor.spec.ts @@ -78,4 +78,4 @@ describe('ContextExecutor', () => { expect(repeatedNotifications).toHaveLength(0) }) -}) \ No newline at end of file +}) diff --git a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts index 75aeb2630..fdcc61a56 100644 --- a/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/BurnMintTokenPool.spec.ts @@ -36,10 +36,7 @@ import { ContextExecutor_ForwardNotification, ContextExecutor_InMessageForward, } from '../../../wrappers/gen/ccip/ContextExecutor' -import { - runTokenPoolAsyncHookBehaviorTests, - runTokenPoolBehaviorTests, -} from './TokenPool.behavior' +import { runTokenPoolAsyncHookBehaviorTests, runTokenPoolBehaviorTests } from './TokenPool.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' @@ -625,7 +622,6 @@ describe('BurnMintTokenPool', () => { }) }) - it('mints tokens on releaseOrMint path and finalizes through the executor notification', async () => { const result = await burnMintPool.sendTokenPoolReleaseOrMint( offRamp.getSender(), diff --git a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts index 723fd7402..78a16bda2 100644 --- a/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseLockboxTokenPool.spec.ts @@ -290,20 +290,24 @@ describe('LockReleaseLockboxTokenPool', () => { }) } - runTokenPoolBehaviorTests('LockReleaseLockboxTokenPool', async () => ({ - pool, - deployer, - offRamp, - unauthorized: recipient, - recipient, - remoteChainSelector, - onRampAddress: deployer.address, - destTokenAddress, - sourcePoolAddress, - localToken: jettonMinter.address, - }), { - setup: setupTokenPoolBehaviorContext, - }) + runTokenPoolBehaviorTests( + 'LockReleaseLockboxTokenPool', + async () => ({ + pool, + deployer, + offRamp, + unauthorized: recipient, + recipient, + remoteChainSelector, + onRampAddress: deployer.address, + destTokenAddress, + sourcePoolAddress, + localToken: jettonMinter.address, + }), + { + setup: setupTokenPoolBehaviorContext, + }, + ) // Async hook behavior tests (TON-TP/6) runTokenPoolAsyncHookBehaviorTests('LockReleaseLockboxTokenPool', async () => { @@ -352,7 +356,6 @@ describe('LockReleaseLockboxTokenPool', () => { expect(await lockReleaseLockboxPool.getToken()).toEqualAddress(jettonMinter.address) expect(await lockReleaseLockboxPool.getTokenDecimals()).toBe(9n) }) - }) describe('lock flow (jetton transfer -> lockbox custody)', () => { @@ -412,7 +415,6 @@ describe('LockReleaseLockboxTokenPool', () => { to: lockReleaseLockboxPool.address, success: true, }) - }) it('should store pending lock when forwarded amount matches transfer amount', async () => { @@ -463,7 +465,6 @@ describe('LockReleaseLockboxTokenPool', () => { forwardPayload: TokenPool_LockOrBurnForwardPayload.toCell(forwardPayload), }, }) - }) }) @@ -516,7 +517,6 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) - // Pool sends JettonLockBox_Withdraw to the lockbox (lockbox will handle the jetton transfer) expect(result.transactions).toHaveTransaction({ from: lockReleaseLockboxPool.address, @@ -651,7 +651,6 @@ describe('LockReleaseLockboxTokenPool', () => { success: true, }) }) - }) describe('full release flow (end-to-end through lockbox)', () => { diff --git a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts index c64b2c539..79a641ec4 100644 --- a/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts +++ b/contracts/tests/ccip/pools/LockReleaseTokenPool.spec.ts @@ -41,10 +41,7 @@ import { } from '../../../wrappers/gen/ccip/ContextExecutor' import * as CrossChainAddressCodec from '../../../wrappers/ccip/common/CrossChainAddressCodec' -import { - runTokenPoolAsyncHookBehaviorTests, - runTokenPoolBehaviorTests, -} from './TokenPool.behavior' +import { runTokenPoolAsyncHookBehaviorTests, runTokenPoolBehaviorTests } from './TokenPool.behavior' import { MockAdvancedPoolHooks } from '../../../wrappers/gen/ccip/test/MockAdvancedPoolHooks' function buildSpoofedExecutorForwardNotification(senderAddress: Address): Cell { @@ -174,26 +171,26 @@ describe('LockReleaseTokenPool', () => { toNano('0.2'), { queryId: 1n, - remoteChainSelectorsToRemove: [], - chainsToAdd: [ + remoteChainSelectorsToRemove: [], + chainsToAdd: [ TokenPool_ChainUpdate.create({ remoteChainSelector, - remotePoolAddresses: [sourcePoolAddress], - remoteTokenAddress: destTokenAddress, - rateLimitConfigs: TokenPool_RateLimitConfigPair.create({ - outbound: RateLimiter_Config.create({ + remotePoolAddresses: [sourcePoolAddress], + remoteTokenAddress: destTokenAddress, + rateLimitConfigs: TokenPool_RateLimitConfigPair.create({ + outbound: RateLimiter_Config.create({ isEnabled: true, capacity: toNano('100'), rate: 1n, - }), - inbound: RateLimiter_Config.create({ + }), + inbound: RateLimiter_Config.create({ isEnabled: true, capacity: toNano('100'), rate: 1n, - }), }), + }), }), - ], + ], }, ) @@ -264,20 +261,24 @@ describe('LockReleaseTokenPool', () => { }) } - runTokenPoolBehaviorTests('LockReleaseTokenPool', async () => ({ - pool, - deployer, - offRamp, - unauthorized: recipient, - recipient, - remoteChainSelector, - onRampAddress: deployer.address, - destTokenAddress, - sourcePoolAddress, - localToken: jettonMinter.address, - }), { - setup: setupTokenPoolBehaviorContext, - }) + runTokenPoolBehaviorTests( + 'LockReleaseTokenPool', + async () => ({ + pool, + deployer, + offRamp, + unauthorized: recipient, + recipient, + remoteChainSelector, + onRampAddress: deployer.address, + destTokenAddress, + sourcePoolAddress, + localToken: jettonMinter.address, + }), + { + setup: setupTokenPoolBehaviorContext, + }, + ) // Async hook behavior tests (TON-TP/6) runTokenPoolAsyncHookBehaviorTests('LockReleaseTokenPool', async () => { @@ -637,7 +638,6 @@ describe('LockReleaseTokenPool', () => { }) }) - it('releases tokens with null replyTo without emitting a response message', async () => { const poolWallet = await userWallet(lockReleasePool.address) const recipientWallet = await userWallet(recipient.address) diff --git a/contracts/tests/ccip/pools/TokenPool.behavior.ts b/contracts/tests/ccip/pools/TokenPool.behavior.ts index 38eaa2ff9..df382eefa 100644 --- a/contracts/tests/ccip/pools/TokenPool.behavior.ts +++ b/contracts/tests/ccip/pools/TokenPool.behavior.ts @@ -899,7 +899,10 @@ export function runTokenPoolAsyncHookBehaviorTests( } const slice = body.beginParse() - return slice.remainingBits >= 32 && slice.preloadUint(32) === TokenPool_ReleaseOrMintFailure.PREFIX + return ( + slice.remainingBits >= 32 && + slice.preloadUint(32) === TokenPool_ReleaseOrMintFailure.PREFIX + ) }) expect(failures).toHaveLength(0) From f55fcce1c3c5ea44fa7bdaf3a4998dcdac5dae48 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Fri, 31 Jul 2026 11:47:14 +0200 Subject: [PATCH 06/11] Fix lint --- .../ccip/pools/burn_mint/contract.tolk | 2 +- .../contracts/ccip/pools/burn_mint/types.tolk | 4 ++-- .../ccip/pools/lock_release/contract.tolk | 2 +- .../ccip/pools/lock_release/types.tolk | 2 +- .../pools/lock_release_lockbox/contract.tolk | 2 +- .../pools/lock_release_lockbox/types.tolk | 4 ++-- .../gen/ccip/pools/BurnMintTokenPool.ts | 22 +++++++++---------- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 10 ++++----- .../gen/ccip/pools/LockReleaseTokenPool.ts | 10 ++++----- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 6ad573cdb..638d7a7ff 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -66,7 +66,7 @@ fun deployBurnMintContextExecutor(executor: AutoDeployAddress): void { bounce: BounceMode.NoBounce, value: BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: beginCell().endCell().beginParse(), + body: createEmptyCell().beginParse(), }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } diff --git a/contracts/contracts/ccip/pools/burn_mint/types.tolk b/contracts/contracts/ccip/pools/burn_mint/types.tolk index 5fc5d416d..27f011c5e 100644 --- a/contracts/contracts/ccip/pools/burn_mint/types.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/types.tolk @@ -10,12 +10,12 @@ const BurnMintTokenPool_BURN_VALUE = ton("0.05"); const BurnMintTokenPool_MINT_VALUE = ton("0.1"); const BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); -struct (0x0d49d523) BurnMintTokenPool_BurnContext { +struct (0xba302a47) BurnMintTokenPool_BurnContext { wallet: address; forwardPayload: Cell; } -struct (0x723f6ea1) BurnMintTokenPool_MintContext { +struct (0xb3d52361) BurnMintTokenPool_MintContext { forwardPayload: Cell; } diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index 4c8e2d804..035b038ea 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -62,7 +62,7 @@ fun deployLockReleaseContextExecutor(executor: AutoDeployAddress): void { bounce: BounceMode.NoBounce, value: LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: beginCell().endCell().beginParse(), + body: createEmptyCell().beginParse(), }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } diff --git a/contracts/contracts/ccip/pools/lock_release/types.tolk b/contracts/contracts/ccip/pools/lock_release/types.tolk index 660b03eba..838c4bc60 100644 --- a/contracts/contracts/ccip/pools/lock_release/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release/types.tolk @@ -9,7 +9,7 @@ const LockReleaseTokenPool_RELEASE_TRANSFER_VALUE = ton("0.05"); const LockReleaseTokenPool_REPLY_VALUE = ton("0.01"); const LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); -struct (0x54e6870c) LockReleaseTokenPool_ReleaseContext { +struct (0xed696f9b) LockReleaseTokenPool_ReleaseContext { forwardPayload: Cell; } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index faa891f85..36a86b43c 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -75,7 +75,7 @@ fun deployLockReleaseLockboxContextExecutor(executor: AutoDeployAddress): void { bounce: BounceMode.NoBounce, value: LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: beginCell().endCell().beginParse(), + body: createEmptyCell().beginParse(), }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk index 9301e734d..42ccebcf8 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk @@ -9,11 +9,11 @@ const LockReleaseLockboxTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); const LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); -struct (0x4b92a8e8) LockReleaseLockboxTokenPool_LockContext { +struct (0x60fdb63b) LockReleaseLockboxTokenPool_LockContext { forwardPayload: Cell; } -struct (0xad5476ce) LockReleaseLockboxTokenPool_ReleaseContext { +struct (0x90230477) LockReleaseLockboxTokenPool_ReleaseContext { forwardPayload: Cell; } diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index b26ffd49e..bc838f178 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -4588,7 +4588,7 @@ export const BurnMintTokenPool_ClaimMinterAdmin = { } /** - > struct (0x0d49d523) BurnMintTokenPool_BurnContext { + > struct (0xba302a47) BurnMintTokenPool_BurnContext { > wallet: address > forwardPayload: Cell > } @@ -4600,7 +4600,7 @@ export interface BurnMintTokenPool_BurnContext { } export const BurnMintTokenPool_BurnContext = { - PREFIX: 0x0d49d523, + PREFIX: 0xba302a47, create(args: { wallet: c.Address @@ -4612,7 +4612,7 @@ export const BurnMintTokenPool_BurnContext = { } }, fromSlice(s: c.Slice): BurnMintTokenPool_BurnContext { - loadAndCheckPrefix32(s, 0x0d49d523, 'BurnMintTokenPool_BurnContext'); + loadAndCheckPrefix32(s, 0xba302a47, 'BurnMintTokenPool_BurnContext'); return { $: 'BurnMintTokenPool_BurnContext', wallet: s.loadAddress(), @@ -4620,7 +4620,7 @@ export const BurnMintTokenPool_BurnContext = { } }, store(self: BurnMintTokenPool_BurnContext, b: c.Builder): void { - b.storeUint(0x0d49d523, 32); + b.storeUint(0xba302a47, 32); b.storeAddress(self.wallet); storeCellRef(self.forwardPayload, b, TokenPool_LockOrBurnForwardPayload.store); }, @@ -4630,7 +4630,7 @@ export const BurnMintTokenPool_BurnContext = { } /** - > struct (0x723f6ea1) BurnMintTokenPool_MintContext { + > struct (0xb3d52361) BurnMintTokenPool_MintContext { > forwardPayload: Cell > } */ @@ -4640,7 +4640,7 @@ export interface BurnMintTokenPool_MintContext { } export const BurnMintTokenPool_MintContext = { - PREFIX: 0x723f6ea1, + PREFIX: 0xb3d52361, create(args: { forwardPayload: TokenPool_ReleaseOrMintForwardPayload @@ -4651,14 +4651,14 @@ export const BurnMintTokenPool_MintContext = { } }, fromSlice(s: c.Slice): BurnMintTokenPool_MintContext { - loadAndCheckPrefix32(s, 0x723f6ea1, 'BurnMintTokenPool_MintContext'); + loadAndCheckPrefix32(s, 0xb3d52361, 'BurnMintTokenPool_MintContext'); return { $: 'BurnMintTokenPool_MintContext', forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, store(self: BurnMintTokenPool_MintContext, b: c.Builder): void { - b.storeUint(0x723f6ea1, 32); + b.storeUint(0xb3d52361, 32); storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, toCell(self: BurnMintTokenPool_MintContext): c.Cell { @@ -4675,8 +4675,8 @@ export type BurnMintTokenPool_ContextExecutorContext = export const BurnMintTokenPool_ContextExecutorContext = { fromSlice(s: c.Slice): BurnMintTokenPool_ContextExecutorContext { - return lookupPrefix(s, 0x0d49d523, 32) ? BurnMintTokenPool_BurnContext.fromSlice(s) : - lookupPrefix(s, 0x723f6ea1, 32) ? BurnMintTokenPool_MintContext.fromSlice(s) : + return lookupPrefix(s, 0xba302a47, 32) ? BurnMintTokenPool_BurnContext.fromSlice(s) : + lookupPrefix(s, 0xb3d52361, 32) ? BurnMintTokenPool_MintContext.fromSlice(s) : throwNonePrefixMatch('BurnMintTokenPool_ContextExecutorContext'); }, store(self: BurnMintTokenPool_ContextExecutorContext, b: c.Builder): void { @@ -4872,7 +4872,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index de7a01fb9..7d3f927a3 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -4257,7 +4257,7 @@ export const TokenPool_RampAccessUpdated = { } /** - > struct (0xad5476ce) LockReleaseLockboxTokenPool_ReleaseContext { + > struct (0x90230477) LockReleaseLockboxTokenPool_ReleaseContext { > forwardPayload: Cell > } */ @@ -4267,7 +4267,7 @@ export interface LockReleaseLockboxTokenPool_ReleaseContext { } export const LockReleaseLockboxTokenPool_ReleaseContext = { - PREFIX: 0xad5476ce, + PREFIX: 0x90230477, create(args: { forwardPayload: TokenPool_ReleaseOrMintForwardPayload @@ -4278,14 +4278,14 @@ export const LockReleaseLockboxTokenPool_ReleaseContext = { } }, fromSlice(s: c.Slice): LockReleaseLockboxTokenPool_ReleaseContext { - loadAndCheckPrefix32(s, 0xad5476ce, 'LockReleaseLockboxTokenPool_ReleaseContext'); + loadAndCheckPrefix32(s, 0x90230477, 'LockReleaseLockboxTokenPool_ReleaseContext'); return { $: 'LockReleaseLockboxTokenPool_ReleaseContext', forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, store(self: LockReleaseLockboxTokenPool_ReleaseContext, b: c.Builder): void { - b.storeUint(0xad5476ce, 32); + b.storeUint(0x90230477, 32); storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, toCell(self: LockReleaseLockboxTokenPool_ReleaseContext): c.Cell { @@ -4823,7 +4823,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 5c30c4c51..0d928d7c0 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -4375,7 +4375,7 @@ export const LockReleaseTokenPool_InMessage = { } /** - > struct (0x54e6870c) LockReleaseTokenPool_ReleaseContext { + > struct (0xed696f9b) LockReleaseTokenPool_ReleaseContext { > forwardPayload: Cell > } */ @@ -4385,7 +4385,7 @@ export interface LockReleaseTokenPool_ReleaseContext { } export const LockReleaseTokenPool_ReleaseContext = { - PREFIX: 0x54e6870c, + PREFIX: 0xed696f9b, create(args: { forwardPayload: TokenPool_ReleaseOrMintForwardPayload @@ -4396,14 +4396,14 @@ export const LockReleaseTokenPool_ReleaseContext = { } }, fromSlice(s: c.Slice): LockReleaseTokenPool_ReleaseContext { - loadAndCheckPrefix32(s, 0x54e6870c, 'LockReleaseTokenPool_ReleaseContext'); + loadAndCheckPrefix32(s, 0xed696f9b, 'LockReleaseTokenPool_ReleaseContext'); return { $: 'LockReleaseTokenPool_ReleaseContext', forwardPayload: loadCellRef(s, TokenPool_ReleaseOrMintForwardPayload.fromSlice), } }, store(self: LockReleaseTokenPool_ReleaseContext, b: c.Builder): void { - b.storeUint(0x54e6870c, 32); + b.storeUint(0xed696f9b, 32); storeCellRef(self.forwardPayload, b, TokenPool_ReleaseOrMintForwardPayload.store); }, toCell(self: LockReleaseTokenPool_ReleaseContext): c.Cell { @@ -4589,7 +4589,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, From f2c3b9913fde08c6a4e91915ab36da095966f68d Mon Sep 17 00:00:00 2001 From: Kristijan Date: Fri, 31 Jul 2026 14:00:01 +0200 Subject: [PATCH 07/11] Update Go bindings --- .../ccip/pools/lock_release/types.tolk | 1 - .../review-lock-release-lockbox-tokenpool.md | 310 ++++++++++++++++++ .../contextexecutor/exitcode_string.go | 24 ++ pkg/ccip/bindings/contextexecutor/types.go | 122 +++++++ .../tokenpool/burnmint/exitcode_string.go | 13 +- .../bindings/tokenpool/burnmint/reader.go | 34 -- pkg/ccip/bindings/tokenpool/burnmint/types.go | 56 ++-- pkg/ccip/bindings/tokenpool/lockbox/types.go | 23 +- .../tokenpool/lockrelease/exitcode_string.go | 10 +- .../bindings/tokenpool/lockrelease/reader.go | 19 +- .../bindings/tokenpool/lockrelease/types.go | 32 +- .../lockreleaselockbox/exitcode_string.go | 27 ++ .../tokenpool/lockreleaselockbox/reader.go | 29 -- .../tokenpool/lockreleaselockbox/types.go | 96 ++++++ 14 files changed, 650 insertions(+), 146 deletions(-) create mode 100644 contracts/reviews/review-lock-release-lockbox-tokenpool.md create mode 100644 pkg/ccip/bindings/contextexecutor/exitcode_string.go create mode 100644 pkg/ccip/bindings/contextexecutor/types.go create mode 100644 pkg/ccip/bindings/tokenpool/lockreleaselockbox/exitcode_string.go create mode 100644 pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go diff --git a/contracts/contracts/ccip/pools/lock_release/types.tolk b/contracts/contracts/ccip/pools/lock_release/types.tolk index 838c4bc60..c5591fddb 100644 --- a/contracts/contracts/ccip/pools/lock_release/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release/types.tolk @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 import "../../../lib/jetton/jetton_client" import "../lib/token_pool/messages" -import "../lib/token_pool/types" const LockReleaseTokenPool_CONTRACT_NAME = "link.chain.ton.ccip.LockReleaseTokenPool".literalSlice(); const LockReleaseTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); diff --git a/contracts/reviews/review-lock-release-lockbox-tokenpool.md b/contracts/reviews/review-lock-release-lockbox-tokenpool.md new file mode 100644 index 000000000..6dbabc602 --- /dev/null +++ b/contracts/reviews/review-lock-release-lockbox-tokenpool.md @@ -0,0 +1,310 @@ +# LockReleaseLockboxTokenPool - Pre-Publish Code Review + +## Executive Summary + +This is a deep, pre-publish review comparing the TON `LockReleaseLockboxTokenPool` implementation against the EVM `LockReleaseTokenPool`. Overall the architecture is sound, but there are several items worth addressing before publish. + +--- + +## Architecture Comparison: TON vs EVM + +### EVM (Synchronous) + +``` +User → Router → LockReleaseTokenPool._lockOrBurn() + └── i_lockBox.deposit() [synchronous, atomic] + └── emit LockedOrBurned() [same tx] +``` + +### TON (Asynchronous Message Chains) + +``` +User → Pool wallet → TransferNotificationForRecipient → pool + └── onLockOrBurnTransferContinue() [store pending lock] + └── AskToTransfer [to pool wallet] + └── TransferNotificationForRecipient [to lockbox wallet] + └── JettonLockBox_Deposited [to pool] + └── onLockDepositCompleted() [finalize + emit event] +``` + +**Key difference**: TON emits `TokenPool_LockedOrBurned` **asynchronously** (when lockbox callback arrives), while EVM emits `LockedOrBurned` **synchronously** (in same call). This is a known TON architectural constraint, not a bug. + +--- + +## 🔴 Critical Issues (Fix Before Publish) + +### 1. Silent Failure on Bounced Lock Transfers + +**Location**: `contract.tolk:193-203` - `onLockTransferBounced` + +```tolk +fun onLockTransferBounced(...) { + val pendingEntry = st.pendingLocks.get(msg.queryId); + if (!pendingEntry.isFound) { return; } + val pending = pendingEntry.loadValue().load(); + st.pendingLocks.delete(msg.queryId); + // TODO: try to best-effort return jettons or emit error event +} +``` + +**Problem**: When the AskToTransfer bounces (lockbox wallet can't accept, insufficient TON, etc.): + +- Pending lock is silently deleted +- NO error event emitted +- NO reply sent to Router/OnRamp +- Jettons are already transferred to the pool's wallet - they're "stuck" in the pool but the lock never completed +- Off-chain relayer will see the lock never finalized and may retry, but the jettons are already on the pool + +**EVM comparison**: EVM is atomic - if the deposit fails, the entire tx reverts. No stuck state possible. + +**Fix options**: + +1. Emit a `TokenPool_LockOrBurnFailed` event with error code +2. Send `TokenPool_ReleaseOrMintFailure` to replyTo (matching release flow pattern) +3. Consider trying to bounce jettons back (best-effort return) + +### 2. Hardcoded TON Values in Messages + +**Location**: `contract.tolk:157, 244` - `onLockOrBurnTransferContinue` and `onReleaseOrMintContinue` + +```tolk +val transfer = createMessage({ + value: ton("0.1"), // Hardcoded! + ... +}); +``` + +**Problem**: `ton("0.1")` is hardcoded for both: + +- Lock flow: AskToTransfer to jetton wallet +- Release flow: JettonLockBox_Withdraw to lockbox + +This doesn't account for: + +- Gas price fluctuations +- Complex forward payloads that need more gas +- Future TON fee changes + +**EVM comparison**: EVM uses `msg.value` from the caller - gas is explicit. + +**Recommendation**: Consider making this configurable, or at minimum add a comment explaining why 0.1 TON is sufficient. + +### 3. Incorrect Error Code in WithdrawFailed Handler + +**Location**: `contract.tolk:287` - `onReleaseWithdrawFailed` + +```tolk +body: TokenPool_ReleaseOrMintFailure { + queryId: msg.queryId, + errorCode: TokenPool_Error.UnsupportedOperation as uint16, +}, +``` + +**Problem**: Uses `TokenPool_Error.UnsupportedOperation` as the error code when the lockbox withdraw failed. This is misleading - `UnsupportedOperation` is for when the pool type can't handle a request, not when the lockbox reports a failure. + +**Recommendation**: Use a more appropriate error code. Consider adding `TokenPool_Error.LockboxWithdrawFailed` to the error enum. + +--- + +## 🟡 Medium Priority Issues + +### 4. No Validation of Lockbox Token Support + +**Location**: Constructor / deployment + +**Problem**: EVM's `LockReleaseTokenPool.sol` validates in constructor: + +```solidity +if (!lockBoxContract.isTokenSupported(address(token))) { + revert InvalidToken(address(token)); +} +``` + +TON's `LockReleaseLockboxTokenPool` does NOT validate that the lockbox supports the token at deployment time. A misconfigured pool could be deployed with an unsupported token. + +**Recommendation**: Add validation during deployment/setup. + +### 5. QueryId Collision Window + +**Problem**: `pendingLocks` and `pendingReleases` are maps keyed by `uint64` queryId. While 64-bit gives a large space, there's no protection against intentional queryId reuse (e.g., if the Router sends duplicate requests with the same queryId). + +**Current mitigation**: Duplicate rejection tests pass (exit codes 48700, 48702). + +**Recommendation**: Consider adding TTL or cleanup for stale pending entries. A pending lock that never gets `JettonLockBox_Deposited` callback could block that queryId forever. + +### 6. `transferInitiator` Trust Model + +**Location**: `token_pool.tolk:834-836` + +```tolk +// TODO: can we trust (any) JettonWallet forward payloads? +// assert(request.amount == msg.jettonAmount, TokenPool_Error.AmountMismatch); + +if (msg.transferInitiator != null) { + self.onLockOrBurnTransferContinue(sender, fwdp); + return; +} +``` + +**Problem**: The pool trusts ANY transfer with `transferInitiator != null`. This means any JettonWallet can forward a crafted `TokenPool_LockOrBurnForwardPayload` to the pool. The pool processes it as a valid lock request. + +**Impact**: While the lockbox validates the operator role, a malicious wallet could: + +1. Send jettons with crafted forward payload +2. Pool forwards to lockbox +3. Lockbox rejects (operator check) +4. Jettons return to pool +5. Pool has no way to track this "bounce" state + +**Recommendation**: At minimum, add validation that `forwardPayload.amount == msg.jettonAmount` before processing. + +### 7. No Test for `onLockDepositCompleted` Path + +**Location**: Full lock flow test + +**Problem**: The test explicitly states: + +> "In the sandbox, the lockbox wallet is a standard jetton wallet (not JettonLockBox contract). It returns excesses but does NOT send JettonLockBox_Deposited back to finalize." + +This means the `onLockDepositCompleted` function is **never tested end-to-end**. The event emission path (`TokenPool_LockedOrBurned`) is untested. + +**Recommendation**: Consider integrating the actual JettonLockBox contract in the full lock flow test (not just the wallet). This may require the JettonLockBox to be deployed as a real contract in the sandbox. + +### 8. Redundant Comment in `onReturnExcessesBack` + +**Location**: `contract.tolk:270, 313` + +The comment block appears **twice** (lines 270-275 and 313-318): + +```tolk +/* --- ReturnExcessesBack handler --- */ +/* + * For lock flow: just accept TON, do nothing. Completion comes from JettonLockBox_Deposited. + * For release flow: this IS the completion signal. Restore context, finalize release flow. + */ +``` + +**Recommendation**: Remove the duplicate. + +--- + +## 🟢 Low Priority / Polish + +### 9. Test: "should return jettons for transfers without transferInitiator" is a Comment-Only Test + +**Location**: Test file + +```typescript +it('should return jettons for transfers without transferInitiator (direct user transfers)', async () => { + // ... 8 lines of comments ... + // No assertions! +}) +``` + +**Recommendation**: Either: + +- Remove this test (the logic is in TokenPool library, not pool-specific) +- Or make it an actual test that verifies the behavior + +### 10. Test File Uses `require('zlib')` Inline + +**Location**: Test file, line 195 + +```typescript +const { crc32 } = require('zlib') +``` + +**Recommendation**: Move to top-level imports for consistency. + +### 11. Unused `lockboxOperator` Treasury Account + +**Location**: Test file + +```typescript +let lockboxOperator: SandboxContract +``` + +Declared but never used. + +### 12. Unused `pool` Variable Alias + +**Location**: Test file + +```typescript +let pool: SandboxContract +``` + +Used for `runTokenPoolBehaviorTests` but not in pool-specific tests. Consider documenting this alias pattern. + +### 13. Consider Adding `getPendingLocksCount` and `getPendingReleasesCount` Getters + +**Current state**: Only `getHasPendingLock(queryId)` and `getHasPendingRelease(queryId)` exist. + +**Recommendation**: Adding count getters would help operators monitor pending flow health and detect stuck flows. + +### 14. Contract Version String + +**Location**: `contract.tolk:24` + +```tolk +version: "0.1.0" +``` + +**Recommendation**: Consider bumping to "1.0.0" for publish, matching EVM's `LockReleaseTokenPool 2.0.0` pattern. + +--- + +## Test Coverage Analysis + +### ✅ Covered Well + +- [x] TokenPool shared behavior tests (21 tests - access control, cursed state, chain config) +- [x] Getter verification (lockbox, token, decimals) +- [x] Pending lock creation (2 tests) +- [x] Pending release creation with JettonLockBox_Withdraw assertion +- [x] Duplicate lock rejection (exit code 48700) +- [x] Duplicate release rejection (exit code 48702) +- [x] Release flow rejection when insufficient lockbox balance +- [x] Full lock flow (partial - through AskToTransfer) +- [x] Full release flow (complete - through ReturnExcessesBack) +- [x] Cursed state blocking + +### ⚠️ Partially Covered + +- [x] Full lock flow: ends at jetton transfer to lockbox wallet, does NOT complete with JettonLockBox_Deposited callback +- [x] `onLockTransferBounced`: not tested (TODO in code) +- [x] `onReleaseWithdrawFailed`: not tested end-to-end (only tested that insufficient balance rejects upfront) + +### ❌ Not Covered + +- [ ] `onLockDepositCompleted` (JettonLockBox_Deposited callback) +- [ ] `TokenPool_LockedOrBurned` event emission +- [ ] `onLockTransferBounced` error path +- [ ] `onReleaseWithdrawFailed` callback from actual lockbox failure +- [ ] QueryId cleanup / TTL +- [ ] Forward payload validation (amount mismatch) + +--- + +## Recommendations Summary (Prioritized) + +### Fix Before Publish + +1. **Add error event + reply in `onLockTransferBounced`** (critical - silent failure) +2. **Fix error code in `onReleaseWithdrawFailed`** (misleading error code) +3. **Remove duplicate comment block** (cleanup) + +### Should Fix Before Publish + +4. **Replace comment-only test with actual test or remove it** +5. **Fix `transferInitiator` trust model** (at minimum add amount validation) +6. **Move `require('zlib')` to top-level import** + +### Nice to Have + +7. Add `getPendingLocksCount` / `getPendingReleasesCount` getters +8. Integrate real JettonLockBox in full lock flow test +9. Bump version to "1.0.0" for publish +10. Remove unused `lockboxOperator` variable +11. Consider configurable gas amounts instead of hardcoded `ton("0.1")` +12. Consider queryId TTL/cleanup mechanism diff --git a/pkg/ccip/bindings/contextexecutor/exitcode_string.go b/pkg/ccip/bindings/contextexecutor/exitcode_string.go new file mode 100644 index 000000000..706846c19 --- /dev/null +++ b/pkg/ccip/bindings/contextexecutor/exitcode_string.go @@ -0,0 +1,24 @@ +// Code generated by "stringer -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go"; DO NOT EDIT. + +package contextexecutor + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ExitCodeOnlyCallableByOwner-26000] +} + +const _ExitCode_name = "OnlyCallableByOwner" + +var _ExitCode_index = [...]uint8{0, 19} + +func (i ExitCode) String() string { + idx := int(i) - 26000 + if i < 26000 || idx >= len(_ExitCode_index)-1 { + return "ExitCode(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ExitCode_name[_ExitCode_index[idx]:_ExitCode_index[idx+1]] +} diff --git a/pkg/ccip/bindings/contextexecutor/types.go b/pkg/ccip/bindings/contextexecutor/types.go new file mode 100644 index 000000000..742954956 --- /dev/null +++ b/pkg/ccip/bindings/contextexecutor/types.go @@ -0,0 +1,122 @@ +package contextexecutor + +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" + + "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" +) + +// --- Constants --- + +const ( + // FacilityID is derived from crc32("link.chain.ton.lib.executor.ContextExecutor") % 640 + 10. + FacilityID = 260 + ErrorCodeBase = FacilityID * 100 +) + +// --- Data types --- + +// Data represents the ContextExecutor contract storage. +// C = context type (generic, encoded as a cell). +// Note: array
in Tolk uses a custom encoding (not standard TLB). +// We encode/decode the entire array as a cell ref for safety. +type Data struct { + ID uint64 `tlb:"## 64"` + Owner *address.Address `tlb:"addr"` + Context *cell.Cell `tlb:"^"` + ForwardFrom *cell.Cell `tlb:"^"` // array
- Tolk uses custom encoding +} + +// --- Messages (incoming) --- + +// Set initializes the executor with context and forwardFrom addresses. +// Only callable by the owner. +// Opcode: 0x44e61eec +type Set struct { + _ tlb.Magic `tlb:"#44e61eec" json:"-"` //nolint:revive // (opcode) should stay uninitialized + QueryID uint64 `tlb:"## 64"` + Context *cell.Cell `tlb:"^"` + ForwardFrom *cell.Cell `tlb:"^"` // array
+} + +// Ask queries the executor for context and forward payload. +// Opcode: 0xcad4d1d0 +type Ask struct { + _ tlb.Magic `tlb:"#cad4d1d0" json:"-"` //nolint:revive // (opcode) should stay uninitialized + QueryID uint64 `tlb:"## 64"` + ForwardPayload *cell.Cell `tlb:"^"` + Done bool `tlb:"bool"` +} + +// --- Messages (outgoing) --- + +// Reply is sent in response to Set or Ask messages. +// Opcode: 0x93e5bbc5 +type Reply struct { + _ tlb.Magic `tlb:"#93e5bbc5" json:"-"` //nolint:revive // (opcode) should stay uninitialized + QueryID uint64 `tlb:"## 64"` + ID uint64 `tlb:"## 64"` + Context *cell.Cell `tlb:"^"` + ForwardFrom *cell.Cell `tlb:"^"` // array
+ ForwardPayload *cell.Cell `tlb:"^"` + Done bool `tlb:"bool"` +} + +// ForwardNotification is sent by the executor to the owner when a message +// arrives from a registered forwardFrom address. The executor clears its +// forwardFrom list before sending this notification (one-shot semantics). +// +// Opcode: 0x55b412b9 +type ForwardNotification struct { + _ tlb.Magic `tlb:"#55b412b9" json:"-"` //nolint:revive // (opcode) should stay uninitialized + ID uint64 `tlb:"## 64"` + Context *cell.Cell `tlb:"^"` + ForwardFrom *cell.Cell `tlb:"^"` // array
(captured before clearing) + Message *cell.Cell `tlb:"^"` // Cell +} + +// --- InMessageForward --- + +// InMessageForward represents an internal message received by the executor. +// This mirrors the Tolk struct ContextExecutor_InMessageForward which is +// a synthetic struct (not a TLB message with an opcode). +// We encode it as a raw cell since it follows InternalMsgAddress encoding, +// not a standard TLB struct. +type InMessageForward struct { + SenderAddress *address.Address `tlb:"addr"` + ValueCoins tlb.Coins `tlb:"int 257"` + ValueExtra *cell.Cell `tlb:"^"` // ExtraCurrenciesMap + OriginalForwardFee tlb.Coins `tlb:"int 257"` + CreatedLT uint64 `tlb:"## 64"` + CreatedAt uint32 `tlb:"## 32"` + Body *cell.Cell `tlb:"^"` +} + +// --- Exit Codes --- + +// ExitCode represents a ContextExecutor-specific error code. +type ExitCode tvm.ExitCode + +//go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go + +const ( + ExitCodeOnlyCallableByOwner ExitCode = iota + FacilityID*100 +) + +// New converts an ExitCode to a tvm.ExitCode. +func (e ExitCode) New() tvm.ExitCode { + return tvm.ExitCode(e) +} + +// --- TLB Registry --- + +var TLBs = tvm.MustNewTLBMap([]any{ + // Incoming + Set{}, + Ask{}, + // Outgoing + Reply{}, + ForwardNotification{}, +}).MustWithStorageType(Data{}) diff --git a/pkg/ccip/bindings/tokenpool/burnmint/exitcode_string.go b/pkg/ccip/bindings/tokenpool/burnmint/exitcode_string.go index 812c4f9f0..83a94d5dc 100644 --- a/pkg/ccip/bindings/tokenpool/burnmint/exitcode_string.go +++ b/pkg/ccip/bindings/tokenpool/burnmint/exitcode_string.go @@ -8,17 +8,14 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[ExitCodePendingBurnAlreadyExists-41200] - _ = x[ExitCodePendingBurnNotFound-41201] - _ = x[ExitCodePendingMintAlreadyExists-41202] - _ = x[ExitCodePendingMintNotFound-41203] - _ = x[ExitCodeUnexpectedBurnConfirmationSender-41204] - _ = x[ExitCodeUnexpectedMintConfirmationSender-41205] + _ = x[ExitCodeUnexpectedBurnBounce-41200] + _ = x[ExitCodeUnexpectedMintBounce-41201] + _ = x[ExitCodeContextExecutorUnavailable-41202] } -const _ExitCode_name = "PendingBurnAlreadyExistsPendingBurnNotFoundPendingMintAlreadyExistsPendingMintNotFoundUnexpectedBurnConfirmationSenderUnexpectedMintConfirmationSender" +const _ExitCode_name = "UnexpectedBurnBounceUnexpectedMintBounceContextExecutorUnavailable" -var _ExitCode_index = [...]uint8{0, 24, 43, 67, 86, 118, 150} +var _ExitCode_index = [...]uint8{0, 20, 40, 66} func (i ExitCode) String() string { idx := int(i) - 41200 diff --git a/pkg/ccip/bindings/tokenpool/burnmint/reader.go b/pkg/ccip/bindings/tokenpool/burnmint/reader.go index e6b4de284..9f0a24bc6 100644 --- a/pkg/ccip/bindings/tokenpool/burnmint/reader.go +++ b/pkg/ccip/bindings/tokenpool/burnmint/reader.go @@ -1,46 +1,12 @@ package burnmint import ( - "fmt" - "math/big" - - "github.com/xssnick/tonutils-go/ton" - "github.com/smartcontractkit/chainlink-ton/cciplib/ccip/bindings/common" - "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/tokenpool" ) // --- Getters from burn_mint/contract.tolk --- -// GetHasPendingBurn checks if there is a pending burn operation for the given query ID. -// -// On-chain: get fun hasPendingBurn(queryId: uint64): bool -var GetHasPendingBurn = tvm.Getter[uint64, bool]{ - Name: "hasPendingBurn", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (bool, error) { - v, err := r.Int(0) - if err != nil { - return false, fmt.Errorf("error getting Int(0) - hasPendingBurn: %w", err) - } - return v.Cmp(big.NewInt(0)) != 0, nil - }), -} - -// GetHasPendingMint checks if there is a pending mint operation for the given query ID. -// -// On-chain: get fun hasPendingMint(queryId: uint64): bool -var GetHasPendingMint = tvm.Getter[uint64, bool]{ - Name: "hasPendingMint", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (bool, error) { - v, err := r.Int(0) - if err != nil { - return false, fmt.Errorf("error getting Int(0) - hasPendingMint: %w", err) - } - return v.Cmp(big.NewInt(0)) != 0, nil - }), -} - // GetTypeAndVersion gets the contract type and version. // // On-chain: get fun typeAndVersion(): (slice, slice) diff --git a/pkg/ccip/bindings/tokenpool/burnmint/types.go b/pkg/ccip/bindings/tokenpool/burnmint/types.go index 5c035f9ab..725d01cff 100644 --- a/pkg/ccip/bindings/tokenpool/burnmint/types.go +++ b/pkg/ccip/bindings/tokenpool/burnmint/types.go @@ -5,29 +5,38 @@ import ( "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/tvm/cell" - "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tlbe" "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/tokenpool" ) // --- Constants --- +const ( + // ClaimAdminValue is the TON value for claiming minter admin. + ClaimAdminValue = 50000000 // 0.05 TON in nanotons + // BurnValue is the TON value for burn operations. + BurnValue = 50000000 // 0.05 TON in nanotons + // MintValue is the TON value for mint operations. + MintValue = 100000000 // 0.1 TON in nanotons + // ContextExecutorDeployValue is the TON value for deploying a ContextExecutor. + ContextExecutorDeployValue = 20000000 // 0.02 TON in nanotons +) + // --- Data types --- -// PendingBurn represents a pending burn operation. -// Corresponds to BurnMintTokenPool_PendingBurn in the Tolk contract. -type PendingBurn struct { - ForwardPayload tokenpool.LockOrBurnForwardPayload `tlb:"."` - ExpectedSender *address.Address `tlb:"addr"` +// BurnContext represents the context for a burn operation managed by ContextExecutor. +// Corresponds to BurnMintTokenPool_BurnContext in the Tolk contract (opcode: 0xba302a47). +type BurnContext struct { + _ tlb.Magic `tlb:"#ba302a47" json:"-"` //nolint:revive // (opcode) should stay uninitialized + Wallet *address.Address `tlb:"addr"` + ForwardPayload tokenpool.LockOrBurnForwardPayload `tlb:"^"` } -// PendingMint represents a pending mint operation. -// Corresponds to BurnMintTokenPool_PendingMint in the Tolk contract. -type PendingMint struct { - ReplyTo *address.Address `tlb:"addr"` - Request tokenpool.ReleaseOrMintInV1 `tlb:"^"` - Out tokenpool.ReleaseOrMintOutV1 `tlb:"^"` - ExpectedSender *address.Address `tlb:"addr"` +// MintContext represents the context for a mint operation managed by ContextExecutor. +// Corresponds to BurnMintTokenPool_MintContext in the Tolk contract (opcode: 0xb3d52361). +type MintContext struct { + _ tlb.Magic `tlb:"#b3d52361" json:"-"` //nolint:revive // (opcode) should stay uninitialized + ForwardPayload tokenpool.ReleaseOrMintForwardPayload `tlb:"^"` } // --- Messages (incoming) --- @@ -48,12 +57,11 @@ type ReturnExcessesBack struct { // --- Storage --- // Storage represents the BurnMintTokenPool contract storage. -// Extends the common TokenPool storage with burn/mint specific state. +// Matches Tolk: struct Storage { poolData: Cell; contextExecutorCode: cell; contextExecutorNextId: uint64; } type Storage struct { - PoolData tokenpool.Storage `tlb:"^"` - JettonClient *cell.Cell `tlb:"^"` // Cell - TODO: define JettonClient type in common/jetton bindings - PendingBurns *tlbe.Dict[uint64, *cell.Cell] `tlb:"dict 64"` // map> - PendingMints *tlbe.Dict[uint64, *cell.Cell] `tlb:"dict 64"` // map> + PoolData tokenpool.Storage `tlb:"^"` + ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment + ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses } // --- Exit Codes --- @@ -65,12 +73,9 @@ type ExitCode tvm.ExitCode //go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go const ( - ExitCodePendingBurnAlreadyExists ExitCode = iota + 41200 // Facility ID 412 * 100 - ExitCodePendingBurnNotFound - ExitCodePendingMintAlreadyExists - ExitCodePendingMintNotFound - ExitCodeUnexpectedBurnConfirmationSender - ExitCodeUnexpectedMintConfirmationSender + ExitCodeUnexpectedBurnBounce ExitCode = iota + 41200 // Facility ID 412 * 100 + ExitCodeUnexpectedMintBounce + ExitCodeContextExecutorUnavailable ) // New converts an ExitCode to a tvm.ExitCode. @@ -84,6 +89,9 @@ var TLBs = tvm.MustNewTLBMap([]any{ // Incoming ClaimMinterAdmin{}, ReturnExcessesBack{}, + // Context types + BurnContext{}, + MintContext{}, }).MustWithStorageType(Storage{}) // --- Standard interface --- diff --git a/pkg/ccip/bindings/tokenpool/lockbox/types.go b/pkg/ccip/bindings/tokenpool/lockbox/types.go index 36ca6d93c..3a218ff8a 100644 --- a/pkg/ccip/bindings/tokenpool/lockbox/types.go +++ b/pkg/ccip/bindings/tokenpool/lockbox/types.go @@ -5,6 +5,7 @@ import ( "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" "github.com/smartcontractkit/chainlink-ton/pkg/bindings/lib/access/rbac" @@ -19,14 +20,16 @@ const OperatorRole = 1735955001 // crc32("OPERATOR_ROLE") type Deposit struct { _ tlb.Magic `tlb:"#9e9ec361" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` - Token *address.Address `tlb:"addr"` // The address of the token to deposit. - RemoteChainSelector uint64 `tlb:"## 64"` // The chain selector of the remote chain. - Amount *big.Int `tlb:"## 256"` // The amount of tokens to deposit. + Token *address.Address `tlb:"addr"` // The address of the token to deposit. + RemoteChainSelector uint64 `tlb:"## 64"` // The chain selector of the remote chain. + Amount *big.Int `tlb:"## 256"` // The amount of tokens to deposit. + Context *cell.Cell `tlb:"maybe ^"` // Optional context carried through the deposit flow. } // WithdrawExtra holds optional extra fields for Withdraw. type WithdrawExtra struct { SendExcessesTo *address.Address `tlb:"addr"` + FailureContext *cell.Cell `tlb:"maybe ^"` // Optional context carried through withdrawal failure chain. } // Withdraws tokens to a specific recipient. @@ -43,9 +46,10 @@ type Withdraw struct { type Deposited struct { _ tlb.Magic `tlb:"#6d077f2e" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` - Token *address.Address `tlb:"addr"` // The token address. - RemoteChainSelector uint64 `tlb:"## 64"` // The chain selector of the remote chain. - Amount *big.Int `tlb:"## 256"` // The amount of tokens deposited. + Token *address.Address `tlb:"addr"` // The token address. + RemoteChainSelector uint64 `tlb:"## 64"` // The chain selector of the remote chain. + Amount *big.Int `tlb:"## 256"` // The amount of tokens deposited. + Context *cell.Cell `tlb:"maybe ^"` // Optional context carried through the deposit flow. } // Init initializes the lockbox with a jetton minter/wallet and admin. @@ -71,9 +75,10 @@ type Initialized struct { type WithdrawFailed struct { _ tlb.Magic `tlb:"#60bae556" json:"-"` //nolint:revive // (opcode) should stay uninitialized QueryID uint64 `tlb:"## 64"` - Token *address.Address `tlb:"addr"` // The token address. - Amount *big.Int `tlb:"## 256"` // The amount that failed to withdraw. - RecipientWallet *address.Address `tlb:"addr"` // The jetton wallet address of the intended recipient. + Token *address.Address `tlb:"addr"` // The token address. + Amount *big.Int `tlb:"## 256"` // The amount that failed to withdraw. + RecipientWallet *address.Address `tlb:"addr"` // The jetton wallet address of the intended recipient. + Context *cell.Cell `tlb:"maybe ^"` // Optional context carried through the failure chain. } // ExitCode represents a JettonLockBox-specific error code. diff --git a/pkg/ccip/bindings/tokenpool/lockrelease/exitcode_string.go b/pkg/ccip/bindings/tokenpool/lockrelease/exitcode_string.go index 4f24700a1..cf9a2a5d2 100644 --- a/pkg/ccip/bindings/tokenpool/lockrelease/exitcode_string.go +++ b/pkg/ccip/bindings/tokenpool/lockrelease/exitcode_string.go @@ -8,15 +8,13 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[ExitCodePendingReleaseAlreadyExists-26300] - _ = x[ExitCodePendingReleaseNotFound-26301] - _ = x[ExitCodeUnexpectedReleaseConfirmationSender-26302] - _ = x[ExitCodeUnexpectedReleaseBounce-26303] + _ = x[ExitCodeUnexpectedReleaseBounce-26300] + _ = x[ExitCodeContextExecutorUnavailable-26301] } -const _ExitCode_name = "PendingReleaseAlreadyExistsPendingReleaseNotFoundUnexpectedReleaseConfirmationSenderUnexpectedReleaseBounce" +const _ExitCode_name = "UnexpectedReleaseBounceContextExecutorUnavailable" -var _ExitCode_index = [...]uint8{0, 27, 49, 84, 107} +var _ExitCode_index = [...]uint8{0, 23, 49} func (i ExitCode) String() string { idx := int(i) - 26300 diff --git a/pkg/ccip/bindings/tokenpool/lockrelease/reader.go b/pkg/ccip/bindings/tokenpool/lockrelease/reader.go index 0d1281b4d..e2fd103f8 100644 --- a/pkg/ccip/bindings/tokenpool/lockrelease/reader.go +++ b/pkg/ccip/bindings/tokenpool/lockrelease/reader.go @@ -2,7 +2,6 @@ package lockrelease import ( "fmt" - "math/big" "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/ton" @@ -14,25 +13,9 @@ import ( // --- Getters from lock_release/contract.tolk --- -// GetHasPendingRelease checks if there is a pending release operation for the given query ID. -// -// On-chain: get fun hasPendingRelease(queryId: uint64): bool -var GetHasPendingRelease = tvm.Getter[uint64, bool]{ - Name: "hasPendingRelease", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (bool, error) { - v, err := r.Int(0) - if err != nil { - return false, fmt.Errorf("error getting Int(0) - hasPendingRelease: %w", err) - } - return v.Cmp(big.NewInt(0)) != 0, nil - }), -} - // GetLockbox gets the lockbox address (for lock_release variant with lockbox support). // -// On-chain: matching EVM getLockBox() -> address -// Note: The base lock_release contract may not expose this, but the lock_release_lockbox variant does. -// This is provided here for consistency with the EVM LockReleaseTokenPool spec. +// On-chain: get fun lockbox(): address var GetLockbox = tvm.NewNoArgsGetter(tvm.NoArgsOpts[*address.Address]{ Name: "lockbox", Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (*address.Address, error) { diff --git a/pkg/ccip/bindings/tokenpool/lockrelease/types.go b/pkg/ccip/bindings/tokenpool/lockrelease/types.go index cbe1565d4..82133bf45 100644 --- a/pkg/ccip/bindings/tokenpool/lockrelease/types.go +++ b/pkg/ccip/bindings/tokenpool/lockrelease/types.go @@ -1,11 +1,9 @@ package lockrelease import ( - "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/tvm/cell" - "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tlbe" "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/tokenpool" ) @@ -20,17 +18,17 @@ const ( ReleaseTransferValue = 50000000 // 0.05 TON in nanotons // ReplyValue is the TON value for reply messages. ReplyValue = 10000000 // 0.01 TON in nanotons + // ContextExecutorDeployValue is the TON value for deploying a ContextExecutor. + ContextExecutorDeployValue = 20000000 // 0.02 TON in nanotons ) // --- Data types --- -// PendingRelease represents a pending release operation. -// Corresponds to LockReleaseTokenPool_PendingRelease in the Tolk contract. -type PendingRelease struct { - ReplyTo *address.Address `tlb:"addr"` - Request *tokenpool.ReleaseOrMintInV1 `tlb:"^"` - Out *tokenpool.ReleaseOrMintOutV1 `tlb:"^"` - ExpectedSender *address.Address `tlb:"addr"` +// ReleaseContext represents the context for a release operation managed by ContextExecutor. +// Corresponds to LockReleaseTokenPool_ReleaseContext in the Tolk contract (opcode: 0xed696f9b). +type ReleaseContext struct { + _ tlb.Magic `tlb:"#ed696f9b" json:"-"` //nolint:revive // (opcode) should stay uninitialized + ForwardPayload tokenpool.ReleaseOrMintForwardPayload `tlb:"^"` } // --- Messages (incoming) --- @@ -45,11 +43,11 @@ type ReturnExcessesBack struct { // --- Storage --- // Storage represents the LockReleaseTokenPool contract storage. -// Extends the common TokenPool storage with lock/release specific state. +// Matches Tolk: struct Storage { poolData: Cell; contextExecutorCode: cell; contextExecutorNextId: uint64; } type Storage struct { - PoolData tokenpool.Storage `tlb:"^"` - JettonClient *cell.Cell `tlb:"^"` // Cell - TODO: define JettonClient type in common/jetton bindings - PendingReleases *tlbe.Dict[uint64, *cell.Cell] `tlb:"dict 64"` // map> + PoolData tokenpool.Storage `tlb:"^"` + ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment + ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses } // --- Exit Codes --- @@ -61,10 +59,8 @@ type ExitCode tvm.ExitCode //go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go const ( - ExitCodePendingReleaseAlreadyExists ExitCode = iota + 26300 // Facility ID 263 * 100 - ExitCodePendingReleaseNotFound - ExitCodeUnexpectedReleaseConfirmationSender - ExitCodeUnexpectedReleaseBounce + ExitCodeUnexpectedReleaseBounce ExitCode = iota + 26300 // Facility ID 263 * 100 + ExitCodeContextExecutorUnavailable ) // New converts an ExitCode to a tvm.ExitCode. @@ -77,6 +73,8 @@ func (e ExitCode) New() tvm.ExitCode { var TLBs = tvm.MustNewTLBMap([]any{ // Incoming ReturnExcessesBack{}, + // Context types + ReleaseContext{}, }).MustWithStorageType(Storage{}) // --- Standard interface --- diff --git a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/exitcode_string.go b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/exitcode_string.go new file mode 100644 index 000000000..db318c5e7 --- /dev/null +++ b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/exitcode_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go"; DO NOT EDIT. + +package lockreleaselockbox + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ExitCodeContextExecutorUnavailable-48700] + _ = x[ExitCodeLockboxNotConfigured-48701] + _ = x[ExitCodeUnexpectedLockboxConfirmationSender-48702] + _ = x[ExitCodeUnexpectedLockBounce-48703] +} + +const _ExitCode_name = "ContextExecutorUnavailableLockboxNotConfiguredUnexpectedLockboxConfirmationSenderUnexpectedLockBounce" + +var _ExitCode_index = [...]uint8{0, 26, 46, 81, 101} + +func (i ExitCode) String() string { + idx := int(i) - 48700 + if i < 48700 || idx >= len(_ExitCode_index)-1 { + return "ExitCode(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ExitCode_name[_ExitCode_index[idx]:_ExitCode_index[idx+1]] +} diff --git a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go index e009e3dea..c270fc735 100644 --- a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go +++ b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/reader.go @@ -2,7 +2,6 @@ package lockreleaselockbox import ( "fmt" - "math/big" "github.com/xssnick/tonutils-go/address" "github.com/xssnick/tonutils-go/ton" @@ -19,34 +18,6 @@ import ( // --- Variant-specific getters --- -// GetHasPendingLock checks if there is a pending lock operation for the given query ID. -// -// On-chain: get fun hasPendingLock(queryId: uint64): bool -var GetHasPendingLock = tvm.Getter[uint64, bool]{ - Name: "hasPendingLock", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (bool, error) { - v, err := r.Int(0) - if err != nil { - return false, fmt.Errorf("error getting Int(0) - hasPendingLock: %w", err) - } - return v.Cmp(big.NewInt(0)) != 0, nil - }), -} - -// GetHasPendingRelease checks if there is a pending release operation for the given query ID. -// -// On-chain: get fun hasPendingRelease(queryId: uint64): bool -var GetHasPendingRelease = tvm.Getter[uint64, bool]{ - Name: "hasPendingRelease", - Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (bool, error) { - v, err := r.Int(0) - if err != nil { - return false, fmt.Errorf("error getting Int(0) - hasPendingRelease: %w", err) - } - return v.Cmp(big.NewInt(0)) != 0, nil - }), -} - // GetLockbox gets the JettonLockBox address used by this token pool. // // On-chain: get fun lockbox(): address diff --git a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go new file mode 100644 index 000000000..167df61c1 --- /dev/null +++ b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go @@ -0,0 +1,96 @@ +package lockreleaselockbox + +import ( + "github.com/xssnick/tonutils-go/address" + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/tvm/cell" + + "github.com/smartcontractkit/chainlink-ton/cciplib/ton/tvm" + "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/tokenpool" +) + +// --- Constants --- + +const ( + // ContextExecutorDeployValue is the TON value for deploying a ContextExecutor. + // Matches Tolk: LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02") + ContextExecutorDeployValue = 20000000 // 0.02 TON in nanotons +) + +// --- Data types --- + +// LockContext represents the context for a lock operation managed by ContextExecutor. +// Corresponds to LockReleaseLockboxTokenPool_LockContext (opcode: 0x60fdb63b). +type LockContext struct { + _ tlb.Magic `tlb:"#60fdb63b" json:"-"` //nolint:revive // (opcode) should stay uninitialized + ForwardPayload tokenpool.LockOrBurnForwardPayload `tlb:"^"` +} + +// ReleaseContext represents the context for a release operation managed by ContextExecutor. +// Corresponds to LockReleaseLockboxTokenPool_ReleaseContext (opcode: 0x90230477). +type ReleaseContext struct { + _ tlb.Magic `tlb:"#90230477" json:"-"` //nolint:revive // (opcode) should stay uninitialized + ForwardPayload tokenpool.ReleaseOrMintForwardPayload `tlb:"^"` +} + +// --- Messages (incoming) --- + +// ReturnExcessesBack is sent by the jetton wallet after a transfer operation. +// TODO: move to shared jetton bindings package, shared opcode 0xd53276db +type ReturnExcessesBack struct { + _ tlb.Magic `tlb:"#d53276db" json:"-"` //nolint:revive // (opcode) should stay uninitialized + QueryID uint64 `tlb:"## 64"` +} + +// --- Storage --- + +// Storage represents the LockReleaseLockboxTokenPool contract storage. +// Matches Tolk: struct Storage { poolData: Cell; lockbox: address; contextExecutorCode: cell; contextExecutorNextId: uint64; } +type Storage struct { + PoolData tokenpool.Storage `tlb:"^"` + Lockbox *address.Address `tlb:"addr"` // JettonLockBox address + ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment + ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses +} + +// --- Exit Codes --- + +// ExitCode represents a LockReleaseLockboxTokenPool-specific error code. +// FACILITY_ID = 487, base error = 48700. +type ExitCode tvm.ExitCode + +//go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode -trimprefix=ExitCode -output=exitcode_string.go + +const ( + ExitCodeContextExecutorUnavailable ExitCode = iota + 48700 // Facility ID 487 * 100 + ExitCodeLockboxNotConfigured + ExitCodeUnexpectedLockboxConfirmationSender + ExitCodeUnexpectedLockBounce +) + +// New converts an ExitCode to a tvm.ExitCode. +func (e ExitCode) New() tvm.ExitCode { + return tvm.ExitCode(e) +} + +// --- TLB Registry --- + +var TLBs = tvm.MustNewTLBMap([]any{ + // Incoming + ReturnExcessesBack{}, + // Context types + LockContext{}, + ReleaseContext{}, +}).MustWithStorageType(Storage{}) + +// --- Standard interface --- + +// Re-import common types from the parent tokenpool package for convenience. +type ( + LockOrBurn = tokenpool.LockOrBurn + ReleaseOrMint = tokenpool.ReleaseOrMint + LockOrBurnTransferDetails = tokenpool.LockOrBurnTransferDetails + ReleaseOrMintTransferDetails = tokenpool.ReleaseOrMintTransferDetails + LockOrBurnForwardPayload = tokenpool.LockOrBurnForwardPayload + ChainSelector = tokenpool.ChainSelector +) From 542c047d96772267dff6cb403c8a8d44ac30e1e5 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Tue, 4 Aug 2026 17:18:25 +0200 Subject: [PATCH 08/11] Share code in TP lib + docs --- .../executors/context_executor/contract.tolk | 3 + .../ccip/executors/context_executor/lib.tolk | 2 + .../executors/context_executor/messages.tolk | 9 + .../executors/context_executor/types.tolk | 14 +- .../ccip/pools/burn_mint/contract.tolk | 138 +++------------ .../ccip/pools/burn_mint/storage.tolk | 6 + .../contracts/ccip/pools/burn_mint/types.tolk | 5 + .../ccip/pools/lib/token_pool/entrypoint.tolk | 164 ++++++++++++++---- .../ccip/pools/lock_release/contract.tolk | 79 +-------- .../ccip/pools/lock_release/storage.tolk | 6 + .../ccip/pools/lock_release/types.tolk | 2 + .../pools/lock_release_lockbox/contract.tolk | 78 ++------- .../pools/lock_release_lockbox/storage.tolk | 7 + .../pools/lock_release_lockbox/types.tolk | 4 + .../gen/ccip/pools/BurnMintTokenPool.ts | 2 +- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 2 +- .../gen/ccip/pools/LockReleaseTokenPool.ts | 2 +- 17 files changed, 236 insertions(+), 287 deletions(-) diff --git a/contracts/contracts/ccip/executors/context_executor/contract.tolk b/contracts/contracts/ccip/executors/context_executor/contract.tolk index 60c0fd235..c2f52dd88 100644 --- a/contracts/contracts/ccip/executors/context_executor/contract.tolk +++ b/contracts/contracts/ccip/executors/context_executor/contract.tolk @@ -5,6 +5,9 @@ import "lib" import "messages" import "types" +/// @notice Generic message interceptor deployed per async operation. +/// Waits for messages from whitelisted `forwardFrom` addresses, then forwards them +/// to the owner pool with the original context attached for finalization. contract ContextExecutor { author: "SmartContract Chainlink Limited SEZC" version: "0.1.0" diff --git a/contracts/contracts/ccip/executors/context_executor/lib.tolk b/contracts/contracts/ccip/executors/context_executor/lib.tolk index ebbe9b107..399c732b7 100644 --- a/contracts/contracts/ccip/executors/context_executor/lib.tolk +++ b/contracts/contracts/ccip/executors/context_executor/lib.tolk @@ -5,6 +5,8 @@ import "../../../lib/utils" import "types" import "messages" +/// @notice Handles incoming messages: Set/Ask are control messages from owner, +/// all other messages from `forwardFrom` addresses are forwarded to owner. fun ContextExecutor.onInternalMessage( mutate self, in: ContextExecutor_InMessageForward, diff --git a/contracts/contracts/ccip/executors/context_executor/messages.tolk b/contracts/contracts/ccip/executors/context_executor/messages.tolk index d99dc565d..d15ca7a23 100644 --- a/contracts/contracts/ccip/executors/context_executor/messages.tolk +++ b/contracts/contracts/ccip/executors/context_executor/messages.tolk @@ -1,12 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 import "types" +/// @notice Sets the context and forwardFrom whitelist on an existing ContextExecutor. +/// Only callable by the owner. struct (0x44e61eec) ContextExecutor_Set { queryId: uint64; context: Cell; forwardFrom: array
; } +/// @notice Queries or updates the ContextExecutor state. +/// When `done=true`, the executor self-destructs after replying. struct (0xcad4d1d0) ContextExecutor_Ask { queryId: uint64; @@ -17,6 +21,7 @@ struct (0xcad4d1d0) ContextExecutor_Ask { done: bool; } +/// @notice Union of all incoming messages the ContextExecutor accepts. type ContextExecutor_InMessage = | ContextExecutor_Set | ContextExecutor_Ask; @@ -35,6 +40,9 @@ struct (0x93e5bbc5) ContextExecutor_Reply { done: bool; } +/// @notice Forwarded notification sent to the owner pool when a message arrives +/// from a whitelisted `forwardFrom` address. Carries the original context and message. +/// @dev After forwarding, `forwardFrom` is cleared so the executor only forwards once. struct (0x55b412b9) ContextExecutor_ForwardNotification { // Notice: no queryId here, because it's not a reply to a specific request @@ -46,6 +54,7 @@ struct (0x55b412b9) ContextExecutor_ForwardNotification { message: Cell; } +/// @notice Union of all outgoing messages the ContextExecutor can send. type ContextExecutor_OutMessage = | ContextExecutor_Reply | ContextExecutor_ForwardNotification; diff --git a/contracts/contracts/ccip/executors/context_executor/types.tolk b/contracts/contracts/ccip/executors/context_executor/types.tolk index 02668c816..97f37b234 100644 --- a/contracts/contracts/ccip/executors/context_executor/types.tolk +++ b/contracts/contracts/ccip/executors/context_executor/types.tolk @@ -2,13 +2,20 @@ import "../../../lib/utils" +/// @notice Runtime data stored by each ContextExecutor instance. +/// A pool deploys one executor per async operation with deterministic address. struct ContextExecutor_Data { - id: uint64; // owner-scoped deployment nonce used to derive a unique executor address + /// 64bit addr space for a unique identity, for more use context C + id: uint64; + /// Address of the owning pool contract (only owner receives forwarded messages). owner: address; + /// Arbitrary context cell encoding operation-specific data for the owner to consume. context: Cell; + /// Whitelist of addresses whose messages will be forwarded to the owner. forwardFrom: array
; } +/// @notice Wrapper providing load/store helpers for ContextExecutor instances. struct ContextExecutor { data: ContextExecutor_Data; } @@ -33,6 +40,8 @@ fun ContextExecutor.getAddress( }; } +/// @notice Forwarded message info passed from ContextExecutor to the owner pool. +/// Contains the original message metadata plus the message body cell. struct ContextExecutor_InMessageForward { senderAddress: address // an internal address from which the message arrived valueCoins: coins // ton amount attached to an incoming message @@ -47,7 +56,8 @@ const ContextExecutor_FACILITY_NAME = "link.chain.ton.lib.executor.ContextExecut const ContextExecutor_FACILITY_ID = ContextExecutor_FACILITY_NAME.crc32() % 640 + 10; const ContextExecutor_ERROR_CODE = ContextExecutor_FACILITY_ID * 100; -/// Error codes used by the ContextExecutor contract. +/// @notice Error codes used by the ContextExecutor contract. +/// Facility ID: 260 (based on FACILITY_NAME CRC32). enum ContextExecutor_Error { /// Thrown when a function is called by an address that is not the owner of the contract. OnlyCallableByOwner = ContextExecutor_ERROR_CODE diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 638d7a7ff..38d260556 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -23,6 +23,9 @@ import "types" import "errors" import "storage" +/// @notice Burn-Mint Token Pool implementation for CCIP. +/// Burns jettons on the source chain and mints them on the destination chain. +/// Uses ContextExecutor contracts for async operation handling. contract BurnMintTokenPool { author: "SmartContract Chainlink Limited SEZC" version: "0.1.0" @@ -61,6 +64,8 @@ fun onInternalMessage(in: InMessage) { } } +/// @notice Deploys a new ContextExecutor at the given AutoDeployAddress. +/// @dev The deploy message carries no body — code and data are embedded in the address. fun deployBurnMintContextExecutor(executor: AutoDeployAddress): void { val deploy = createMessage({ bounce: BounceMode.NoBounce, @@ -71,8 +76,9 @@ fun deployBurnMintContextExecutor(executor: AutoDeployAddress): void { deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Handles bounced burn/mint messages: refunds rate limit and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { - val st = Storage.load(); + var st = Storage.load(); val rich = lazy RichBounceBody.fromSlice(in.bouncedBody); val msg = lazy BurnMintTokenPool_BouncedMessage.fromCell(rich.originalBody); match (msg) { @@ -82,10 +88,11 @@ fun onBouncedMessage(in: InMessageBounced) { assert(in.senderAddress == context.wallet) throw BurnMintTokenPool_Error.UnexpectedBurnBounce; val operation = context.forwardPayload.load(); - var mutableSt = st; - refundLockOrBurnRateLimit(mutate mutableSt, operation); - mutableSt.store(); - sendLockOrBurnFailure(operation, BurnMintTokenPool_Error.UnexpectedBurnBounce as uint16); + var pool = loadPool(st); + pool.refundLockOrBurnRateLimit(operation); + st.poolData = pool.data.toCell(); + pool.sendLockOrBurnFailure(operation, BurnMintTokenPool_Error.UnexpectedBurnBounce as uint16); + st.store(); } MintNewJettons => { assert(in.senderAddress == st.poolData.load().jettonClient().masterAddress) throw BurnMintTokenPool_Error.UnexpectedMintBounce; @@ -95,10 +102,11 @@ fun onBouncedMessage(in: InMessageBounced) { val context = lazy BurnMintTokenPool_MintContext.fromCell(contextCell!); val operation = context.forwardPayload.load(); - var mutableSt = st; - refundReleaseOrMintRateLimit(mutate mutableSt, operation); - mutableSt.store(); - sendReleaseOrMintFailure(operation, BurnMintTokenPool_Error.UnexpectedMintBounce as uint16); + var pool = loadPool(st); + pool.refundReleaseOrMintRateLimit(operation); + st.poolData = pool.data.toCell(); + pool.sendReleaseOrMintFailure(operation, BurnMintTokenPool_Error.UnexpectedMintBounce as uint16); + st.store(); } } } @@ -212,59 +220,8 @@ fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { return st; } -fun refundLockOrBurnRateLimit( - mutate st: Storage, - forwardPayload: TokenPool_LockOrBurnForwardPayload, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - var pool = loadPool(st); - if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - pool.releaseFastFinalityOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); - } else { - pool.releaseOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); - } - st.poolData = pool.data.toCell(); -} - -fun refundReleaseOrMintRateLimit( - mutate st: Storage, - forwardPayload: TokenPool_ReleaseOrMintForwardPayload, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - var pool = loadPool(st); - if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - pool.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } else { - pool.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } - st.poolData = pool.data.toCell(); -} - -fun sendLockOrBurnFailure( - forwardPayload: TokenPool_LockOrBurnForwardPayload, - errorCode: uint16, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - if (requestMsg.replyTo != null) { - val fail = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_LockOrBurnFailure { - queryId: requestMsg.queryId, - errorCode, - }, - }); - fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - +/// @notice Transfers jettons back to the original sender after a failed lock/burn. +/// @dev Sends AskToTransfer to the jetton wallet with original sender as recipient. fun returnLockOrBurnTransfer(sender: address, forwardPayload: TokenPool_LockOrBurnForwardPayload) { val requestMsg = lazy forwardPayload.requestMsg.load(); val request = lazy requestMsg.request.load(); @@ -286,56 +243,6 @@ fun returnLockOrBurnTransfer(sender: address, forwardPayload: TokenPool_LockOrBu returnTransfer.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } -fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - val out = prepared.out; - - emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { - remoteChainSelector: details.remoteChainSelector, - details: TokenPool_ReleasedOrMintedDetails { - token: details.localToken, - sender: contract.getAddress(), - amount: out.destinationAmount, - recipient: details.receiver.toCell(), - }.toCell(), - }); - - if (requestMsg.replyTo != null) { - val notify = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFinished { - queryId: requestMsg.queryId, - out: prepared.out.toCell(), - }, - }); - notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - -fun sendReleaseOrMintFailure( - forwardPayload: TokenPool_ReleaseOrMintForwardPayload, - errorCode: uint16, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - if (requestMsg.replyTo != null) { - val fail = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFailure { - queryId: requestMsg.queryId, - errorCode, - }, - }); - fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - fun onClaimMinterAdmin( mutate st: Storage, sender: address, @@ -352,6 +259,10 @@ fun onClaimMinterAdmin( claim.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } +/// @notice Handles ForwardNotification from ContextExecutor after burn or mint completion. +/// Verifies executor identity, then finalizes the operation based on context type. +/// @dev For burn context: calls onLockOrBurnTransferFinalize. +/// For mint context: emits event and sends ReleaseOrMintFinished reply. fun onContextExecutorForwardNotification( mutate st: Storage, sender: address, @@ -385,7 +296,8 @@ fun onContextExecutorForwardNotification( val operation = context.forwardPayload.load(); assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; - sendReleaseOrMintFinished(operation); + var pool = loadPool(st); + pool.sendReleaseOrMintFinished(operation); } } } diff --git a/contracts/contracts/ccip/pools/burn_mint/storage.tolk b/contracts/contracts/ccip/pools/burn_mint/storage.tolk index 38828bc08..97d3e52e6 100644 --- a/contracts/contracts/ccip/pools/burn_mint/storage.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/storage.tolk @@ -6,18 +6,24 @@ import "../lib/token_pool/types" import "types" +/// @notice Pool storage: common pool data + ContextExecutor deployment state. struct Storage { poolData: Cell; + /// Compiled code cell of the ContextExecutor contract (deployed per-operation). contextExecutorCode: cell; + /// Monotonically increasing ID for deterministic ContextExecutor address derivation. contextExecutorNextId: uint64; } @inline +/// @notice Returns true if the next ContextExecutor ID has not reached max uint64. fun Storage.canTakeContextExecutorNextId(self): bool { return self.contextExecutorNextId != 18446744073709551615; } @inline +/// @notice Atomically takes and increments the next ContextExecutor ID. +/// @dev Throws if the ID counter has reached its maximum value. fun Storage.takeContextExecutorNextId(mutate self): uint64 { val id = self.contextExecutorNextId; assert(id != 18446744073709551615) throw 0xFFFF; diff --git a/contracts/contracts/ccip/pools/burn_mint/types.tolk b/contracts/contracts/ccip/pools/burn_mint/types.tolk index 27f011c5e..f19461dfb 100644 --- a/contracts/contracts/ccip/pools/burn_mint/types.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/types.tolk @@ -10,15 +10,20 @@ const BurnMintTokenPool_BURN_VALUE = ton("0.05"); const BurnMintTokenPool_MINT_VALUE = ton("0.1"); const BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); +/// @notice Context passed to ContextExecutor for burn (lock) operations. +/// Carries the jetton wallet address and the full forward payload for finalization. struct (0xba302a47) BurnMintTokenPool_BurnContext { wallet: address; forwardPayload: Cell; } +/// @notice Context passed to ContextExecutor for mint (release) operations. +/// Carries the full forward payload for post-mint finalization. struct (0xb3d52361) BurnMintTokenPool_MintContext { forwardPayload: Cell; } +/// @notice Union of all ContextExecutor contexts used by BurnMintTokenPool. type BurnMintTokenPool_ContextExecutorContext = | BurnMintTokenPool_BurnContext | BurnMintTokenPool_MintContext; diff --git a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk index 5b92144d0..ca985ead0 100644 --- a/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk +++ b/contracts/contracts/ccip/pools/lib/token_pool/entrypoint.tolk @@ -662,31 +662,12 @@ fun TokenPool.onPreflightCheckFailed( msg: TokenPool_PreflightCheckFailed, ): void { val fwdp = msg.forwardPayload.load(); - val requestMsg = lazy fwdp.requestMsg.load(); - val prepared = lazy fwdp.prepared.load(); // Refund outbound rate limit that was consumed before the hook check. - val request = requestMsg.request.load(); - val details = request.transfer.details.load(); - if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - self.releaseFastFinalityOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); - } else { - self.releaseOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); - } + self.refundLockOrBurnRateLimit(fwdp); // Send failure response back to the executor (replyTo). - if (requestMsg.replyTo != null) { - val failure = createMessage({ - bounce: false, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_LockOrBurnFailure { - queryId: requestMsg.queryId, - errorCode: TokenPool_Error.UnsupportedOperation as uint16, - }, - }); - failure.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } + self.sendLockOrBurnFailure(fwdp, TokenPool_Error.UnsupportedOperation as uint16); } /// Resumes the ReleaseOrMint flow after the advanced hooks confirmed the postflight check. @@ -713,30 +694,145 @@ fun TokenPool.onPostflightCheckFailed( msg: TokenPool_PostflightCheckFailed, ): void { val fwdp = msg.forwardPayload.load(); - val requestMsg = lazy fwdp.requestMsg.load(); - val prepared = lazy fwdp.prepared.load(); // Refund inbound rate limit that was consumed before the hook check. - val request = requestMsg.request.load(); - val details = request.transfer.details.load(); - if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - self.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } else { - self.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } + self.refundReleaseOrMintRateLimit(fwdp); // Send failure response back to the executor (replyTo). + self.sendReleaseOrMintFailure(fwdp, TokenPool_Error.UnsupportedOperation as uint16); +} + +// ================================================================ +// │ Common notification & rate-limit refund helpers │ +// ================================================================ + +/// Sends `LockOrBurnFailure` reply to the executor (replyTo address) when an outbound +/// operation fails after admission. +/// @param forwardPayload The full forward payload carrying the original request message. +/// @param errorCode The pool-specific error code to attach to the failure message. +/// @dev Only sends a message if `replyTo` is set on the original request. Instance method +/// so concrete pools can override (e.g. reserve balance before sending). +fun TokenPool.sendLockOrBurnFailure( + self, + forwardPayload: TokenPool_LockOrBurnForwardPayload, + errorCode: uint16, +): void { + val requestMsg = lazy forwardPayload.requestMsg.load(); + if (requestMsg.replyTo != null) { + val failure = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_LockOrBurnFailure { + queryId: requestMsg.queryId, + errorCode, + }, + }); + failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } +} + +/// Emits `ReleasedOrMinted` event and sends `ReleaseOrMintFinished` reply to the executor +/// when an inbound operation completes successfully. +/// @param forwardPayload The full forward payload carrying the original request and prepared data. +/// @dev Event is always emitted. Reply message is only sent if `replyTo` is set. Instance method +/// so concrete pools can override (e.g. customize event or reply body). +fun TokenPool.sendReleaseOrMintFinished( + self, + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, +): void { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + + emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { + remoteChainSelector: details.remoteChainSelector, + details: TokenPool_ReleasedOrMintedDetails { + token: details.localToken, + sender: contract.getAddress(), + amount: prepared.out.destinationAmount, + recipient: details.receiver.toCell(), + }.toCell(), + }); + + if (requestMsg.replyTo != null) { + val notify = createMessage({ + bounce: BounceMode.NoBounce, + value: 0, + dest: requestMsg.replyTo!, + body: TokenPool_ReleaseOrMintFinished { + queryId: requestMsg.queryId, + out: prepared.out.toCell(), + }, + }); + notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } +} + +/// Sends `ReleaseOrMintFailure` reply to the executor (replyTo address) when an inbound +/// operation fails after admission. +/// @param forwardPayload The full forward payload carrying the original request message. +/// @param errorCode The pool-specific error code to attach to the failure message. +/// @dev Only sends a message if `replyTo` is set on the original request. Instance method +/// so concrete pools can override (e.g. reserve balance before sending). +fun TokenPool.sendReleaseOrMintFailure( + self, + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, + errorCode: uint16, +): void { + val requestMsg = lazy forwardPayload.requestMsg.load(); if (requestMsg.replyTo != null) { val failure = createMessage({ - bounce: false, // TODO: ? + bounce: BounceMode.NoBounce, value: 0, dest: requestMsg.replyTo!, body: TokenPool_ReleaseOrMintFailure { queryId: requestMsg.queryId, - errorCode: TokenPool_Error.UnsupportedOperation as uint16, + errorCode, }, }); - failure.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + } +} + +/// Refunds the outbound (or fast-finality outbound) rate limit capacity that was consumed +/// before a LockOrBurn operation when it later fails. +/// @param forwardPayload The forward payload carrying the original request and prepared data. +/// @dev Picks the correct rate limiter bucket based on the requested finality config. +fun TokenPool.refundLockOrBurnRateLimit( + mutate self, + forwardPayload: TokenPool_LockOrBurnForwardPayload, +): void { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + + if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { + self.releaseFastFinalityOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); + } else { + self.releaseOutboundRateLimit(details.remoteChainSelector, prepared.destTokenAmount); + } +} + +/// Refunds the inbound (or fast-finality inbound) rate limit capacity that was consumed +/// before a ReleaseOrMint operation when it later fails. +/// @param forwardPayload The forward payload carrying the original request and prepared data. +/// @dev Picks the correct rate limiter bucket based on the requested finality config. +fun TokenPool.refundReleaseOrMintRateLimit( + mutate self, + forwardPayload: TokenPool_ReleaseOrMintForwardPayload, +): void { + val requestMsg = lazy forwardPayload.requestMsg.load(); + val request = lazy requestMsg.request.load(); + val details = lazy request.transfer.details.load(); + val prepared = lazy forwardPayload.prepared.load(); + + if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { + self.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); + } else { + self.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); } } @@ -1071,7 +1167,7 @@ fun TokenPool.notifyLockOrBurnFailure( val requestMsg = lazy forwardPayload.requestMsg.load(); if (requestMsg.replyTo != null) { val failure = createMessage({ - bounce: false, + bounce: BounceMode.NoBounce, value: 0, dest: requestMsg.replyTo!, body: TokenPool_LockOrBurnFailure { diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index 035b038ea..8429c2594 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -67,6 +67,7 @@ fun deployLockReleaseContextExecutor(executor: AutoDeployAddress): void { deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Handles bounced release messages: refunds rate limit and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { var st = Storage.load(); val rich = lazy RichBounceBody.fromSlice(in.bouncedBody); @@ -143,54 +144,6 @@ fun onReleaseOrMint(st: Storage, _msg: TokenPool_ReleaseOrMint): Storage { return st; } -fun refundReleaseOrMintRateLimit( - mutate st: Storage, - forwardPayload: TokenPool_ReleaseOrMintForwardPayload, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - var pool = loadPool(st); - if (requestMsg.requestedFinalityConfig != TOKEN_POOL_WAIT_FOR_FINALITY_FLAG) { - pool.releaseFastFinalityInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } else { - pool.releaseInboundRateLimit(details.remoteChainSelector, prepared.localAmount); - } - st.poolData = pool.data.toCell(); -} - -fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - val out = prepared.out; - - emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { - remoteChainSelector: details.remoteChainSelector, - details: TokenPool_ReleasedOrMintedDetails { - token: details.localToken, - sender: contract.getAddress(), - amount: out.destinationAmount, - recipient: details.receiver.toCell(), - }.toCell(), - }); - - if (requestMsg.replyTo != null) { - val notify = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFinished { - queryId: requestMsg.queryId, - out: prepared.out.toCell(), - }, - }); - notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - fun onReleaseTransferBounced( mutate st: Storage, sender: address, @@ -201,29 +154,14 @@ fun onReleaseTransferBounced( val context = lazy LockReleaseTokenPool_ReleaseContext.fromCell(msg.customPayload!); val operation = context.forwardPayload.load(); - refundReleaseOrMintRateLimit(mutate st, operation); - sendReleaseOrMintFailure(operation, Error.UnexpectedReleaseBounce as uint16); -} - -fun sendReleaseOrMintFailure( - forwardPayload: TokenPool_ReleaseOrMintForwardPayload, - errorCode: uint16, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - if (requestMsg.replyTo != null) { - val fail = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFailure { - queryId: requestMsg.queryId, - errorCode, - }, - }); - fail.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } + var pool = loadPool(st); + pool.refundReleaseOrMintRateLimit(operation); + st.poolData = pool.data.toCell(); + pool.sendReleaseOrMintFailure(operation, Error.UnexpectedReleaseBounce as uint16); } +/// @notice Handles ForwardNotification from ContextExecutor after release completion. +/// Verifies executor identity, then emits event and sends ReleaseOrMintFinished reply. fun onContextExecutorForwardNotification( mutate st: Storage, sender: address, @@ -245,7 +183,8 @@ fun onContextExecutorForwardNotification( val operation = context.forwardPayload.load(); assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; - sendReleaseOrMintFinished(operation); + var pool = loadPool(st); + pool.sendReleaseOrMintFinished(operation); } fun loadPool(st: Storage): TokenPool { diff --git a/contracts/contracts/ccip/pools/lock_release/storage.tolk b/contracts/contracts/ccip/pools/lock_release/storage.tolk index 6c0886b15..69a9cddf3 100644 --- a/contracts/contracts/ccip/pools/lock_release/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release/storage.tolk @@ -6,18 +6,24 @@ import "../lib/token_pool/entrypoint" import "types" +/// @notice Pool storage: common pool data + ContextExecutor deployment state. struct Storage { poolData: Cell; + /// Compiled code cell of the ContextExecutor contract (deployed per-operation). contextExecutorCode: cell; + /// Monotonically increasing ID for deterministic ContextExecutor address derivation. contextExecutorNextId: uint64; } @inline +/// @notice Returns true if the next ContextExecutor ID has not reached max uint64. fun Storage.canTakeContextExecutorNextId(self): bool { return self.contextExecutorNextId != 18446744073709551615; } @inline +/// @notice Atomically takes and increments the next ContextExecutor ID. +/// @dev Throws if the ID counter has reached its maximum value. fun Storage.takeContextExecutorNextId(mutate self): uint64 { val id = self.contextExecutorNextId; assert(id != 18446744073709551615) throw 0xFFFF; diff --git a/contracts/contracts/ccip/pools/lock_release/types.tolk b/contracts/contracts/ccip/pools/lock_release/types.tolk index c5591fddb..dbcc4ae6f 100644 --- a/contracts/contracts/ccip/pools/lock_release/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release/types.tolk @@ -8,6 +8,8 @@ const LockReleaseTokenPool_RELEASE_TRANSFER_VALUE = ton("0.05"); const LockReleaseTokenPool_REPLY_VALUE = ton("0.01"); const LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); +/// @notice Context passed to ContextExecutor for release (mint) operations. +/// Carries the full forward payload for post-release finalization. struct (0xed696f9b) LockReleaseTokenPool_ReleaseContext { forwardPayload: Cell; } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index 36a86b43c..f91acd354 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -80,6 +80,7 @@ fun deployLockReleaseLockboxContextExecutor(executor: AutoDeployAddress): void { deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Handles bounced lock (deposit) messages: returns jettons and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { val msg = lazy LockReleaseLockboxTokenPool_BouncedMessage.fromSlice(in.bouncedBody.skipBouncedPrefix()); match (msg) { @@ -183,8 +184,10 @@ fun onLockTransferBounced( assert(st.poolData.load().jettonClient().isWallet(sender)) throw LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce; assert(msg.customPayload != null) throw LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce; val context = lazy LockReleaseLockboxTokenPool_LockContext.fromCell(msg.customPayload!); - returnLockOrBurnTransfer(st, context.forwardPayload.load()); - sendLockOrBurnFailure(context.forwardPayload.load(), LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce as uint16); + val operation = context.forwardPayload.load(); + returnLockOrBurnTransfer(st, operation); + var pool = loadPool(st); + pool.sendLockOrBurnFailure(operation, LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce as uint16); } /* === Release (withdraw) flow === */ @@ -289,7 +292,8 @@ fun onWithdrawFailed( assert(msg.context != null, LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender); val context = lazy LockReleaseLockboxTokenPool_ReleaseContext.fromCell(msg.context!); assert(msg.queryId == context.forwardPayload.load().requestMsg.load().queryId) throw 0xFFFF; - sendReleaseOrMintFailure(context.forwardPayload.load(), TokenPool_Error.UnsupportedOperation as uint16); + var pool = loadPool(st); + pool.sendReleaseOrMintFailure(context.forwardPayload.load(), TokenPool_Error.UnsupportedOperation as uint16); } fun onReturnExcessesBack( @@ -301,6 +305,8 @@ fun onReturnExcessesBack( // routed to a Context Executor and never complete directly at the pool. } +/// @notice Handles ForwardNotification from ContextExecutor after release (withdraw) completion. +/// Verifies executor identity, then emits event and sends ReleaseOrMintFinished reply. fun onContextExecutorForwardNotification( mutate st: Storage, sender: address, @@ -320,25 +326,12 @@ fun onContextExecutorForwardNotification( val operation = msg.context.load().forwardPayload.load(); assert(returnExcesses.queryId == operation.requestMsg.load().queryId) throw 0xFFFF; - sendReleaseOrMintFinished(operation); -} - -fun sendLockOrBurnFailure( - forwardPayload: TokenPool_LockOrBurnForwardPayload, - errorCode: uint16, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - if (requestMsg.replyTo != null) { - val failure = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_LockOrBurnFailure { queryId: requestMsg.queryId, errorCode }, - }); - failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } + var pool = loadPool(st); + pool.sendReleaseOrMintFinished(operation); } +/// @notice Returns jettons to the original sender after a failed lock (deposit) operation. +/// @dev Sends AskToTransfer to the pool's jetton wallet with original sender as recipient. fun returnLockOrBurnTransfer(st: Storage, forwardPayload: TokenPool_LockOrBurnForwardPayload) { val requestMsg = lazy forwardPayload.requestMsg.load(); val request = lazy requestMsg.request.load(); @@ -360,51 +353,6 @@ fun returnLockOrBurnTransfer(st: Storage, forwardPayload: TokenPool_LockOrBurnFo returnTransfer.send(SEND_MODE_PAY_FEES_SEPARATELY); } -fun sendReleaseOrMintFinished(forwardPayload: TokenPool_ReleaseOrMintForwardPayload) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - val request = lazy requestMsg.request.load(); - val details = lazy request.transfer.details.load(); - val prepared = lazy forwardPayload.prepared.load(); - - emit(TOPIC_TOKEN_POOL_RELEASED_OR_MINTED, TokenPool_ReleasedOrMinted { - remoteChainSelector: details.remoteChainSelector, - details: TokenPool_ReleasedOrMintedDetails { - token: details.localToken, - sender: contract.getAddress(), - amount: prepared.out.destinationAmount, - recipient: details.receiver.toCell(), - }.toCell(), - }); - if (requestMsg.replyTo != null) { - val notify = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFinished { - queryId: requestMsg.queryId, - out: prepared.out.toCell(), - }, - }); - notify.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - -fun sendReleaseOrMintFailure( - forwardPayload: TokenPool_ReleaseOrMintForwardPayload, - errorCode: uint16, -) { - val requestMsg = lazy forwardPayload.requestMsg.load(); - if (requestMsg.replyTo != null) { - val failure = createMessage({ - bounce: BounceMode.NoBounce, - value: 0, - dest: requestMsg.replyTo!, - body: TokenPool_ReleaseOrMintFailure { queryId: requestMsg.queryId, errorCode }, - }); - failure.send(SEND_MODE_PAY_FEES_SEPARATELY | SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); - } -} - /* === Pool loader === */ fun loadPool(st: Storage): TokenPool { diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk index 247fd5bb4..e3c49b4fe 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/storage.tolk @@ -7,19 +7,26 @@ import "../lib/token_pool/entrypoint" import "types" +/// @notice Pool storage: common pool data, lockbox reference, + ContextExecutor deployment state. struct Storage { poolData: Cell; + /// Address of the JettonLockBox contract used for deposit/withdraw operations. lockbox: address; + /// Compiled code cell of the ContextExecutor contract (deployed per-operation). contextExecutorCode: cell; + /// Monotonically increasing ID for deterministic ContextExecutor address derivation. contextExecutorNextId: uint64; } @inline +/// @notice Returns true if the next ContextExecutor ID has not reached max uint64. fun Storage.canTakeContextExecutorNextId(self): bool { return self.contextExecutorNextId != 18446744073709551615; } @inline +/// @notice Atomically takes and increments the next ContextExecutor ID. +/// @dev Throws if the ID counter has reached its maximum value. fun Storage.takeContextExecutorNextId(mutate self): uint64 { val id = self.contextExecutorNextId; assert(id != 18446744073709551615) throw 0xFFFF; diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk index 42ccebcf8..5a03812da 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/types.tolk @@ -9,10 +9,14 @@ const LockReleaseLockboxTokenPool_CONTRACT_VERSION = "0.1.0".literalSlice(); const LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE = ton("0.02"); +/// @notice Context passed to ContextExecutor for lock (deposit) operations. +/// Carries the full forward payload for post-lock finalization. struct (0x60fdb63b) LockReleaseLockboxTokenPool_LockContext { forwardPayload: Cell; } +/// @notice Context passed to ContextExecutor for release (withdraw) operations. +/// Carries the full forward payload for post-release finalization. struct (0x90230477) LockReleaseLockboxTokenPool_ReleaseContext { forwardPayload: Cell; } diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index bc838f178..607457cad 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -4872,7 +4872,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 7d3f927a3..8f2f404a5 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -4823,7 +4823,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index 0d928d7c0..e4a0d6486 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -4589,7 +4589,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, From 7a749750e24763f8f47aebfc4ded99abdce40e6e Mon Sep 17 00:00:00 2001 From: Kristijan Date: Tue, 4 Aug 2026 21:30:29 +0200 Subject: [PATCH 09/11] CE - wait for deploy confirmation before continuation --- .../executors/context_executor/messages.tolk | 4 +- .../executors/context_executor/types.tolk | 2 - .../ccip/pools/burn_mint/contract.tolk | 151 +++++++++---- .../ccip/pools/burn_mint/errors.tolk | 1 + .../ccip/pools/burn_mint/messages.tolk | 1 + .../ccip/pools/lock_release/contract.tolk | 84 +++++-- .../ccip/pools/lock_release/errors.tolk | 1 + .../ccip/pools/lock_release/messages.tolk | 1 + .../pools/lock_release_lockbox/contract.tolk | 96 +++++--- .../pools/lock_release_lockbox/errors.tolk | 1 + .../pools/lock_release_lockbox/messages.tolk | 1 + .../wrappers/gen/ccip/ContextExecutor.ts | 24 +- .../gen/ccip/pools/BurnMintTokenPool.ts | 142 +++++++++++- .../ccip/pools/LockReleaseLockboxTokenPool.ts | 142 +++++++++++- .../gen/ccip/pools/LockReleaseTokenPool.ts | 210 ++++++++++++++---- 15 files changed, 717 insertions(+), 144 deletions(-) diff --git a/contracts/contracts/ccip/executors/context_executor/messages.tolk b/contracts/contracts/ccip/executors/context_executor/messages.tolk index d15ca7a23..bcf0afd1b 100644 --- a/contracts/contracts/ccip/executors/context_executor/messages.tolk +++ b/contracts/contracts/ccip/executors/context_executor/messages.tolk @@ -15,7 +15,7 @@ struct (0xcad4d1d0) ContextExecutor_Ask { queryId: uint64; // Per request context - forwardPayload: cell; + forwardPayload: cell?; // Close the context executor after this request is processed. done: bool; @@ -34,7 +34,7 @@ struct (0x93e5bbc5) ContextExecutor_Reply { forwardFrom: array
; // Per request context - forwardPayload: cell; + forwardPayload: cell?; // Whether the context executor was closed. done: bool; diff --git a/contracts/contracts/ccip/executors/context_executor/types.tolk b/contracts/contracts/ccip/executors/context_executor/types.tolk index 97f37b234..1ae0146e9 100644 --- a/contracts/contracts/ccip/executors/context_executor/types.tolk +++ b/contracts/contracts/ccip/executors/context_executor/types.tolk @@ -1,7 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -import "../../../lib/utils" - /// @notice Runtime data stored by each ContextExecutor instance. /// A pool deploys one executor per async operation with deterministic address. struct ContextExecutor_Data { diff --git a/contracts/contracts/ccip/pools/burn_mint/contract.tolk b/contracts/contracts/ccip/pools/burn_mint/contract.tolk index 38d260556..7e1eb9fda 100644 --- a/contracts/contracts/ccip/pools/burn_mint/contract.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/contract.tolk @@ -54,6 +54,10 @@ fun onInternalMessage(in: InMessage) { onClaimMinterAdmin(mutate st, in.senderAddress, msg.queryId); st.store(); } + ContextExecutor_Reply => { + onContextExecutorReply(mutate st, in.senderAddress, msg); + st.store(); + } ContextExecutor_ForwardNotification => { onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); @@ -64,18 +68,112 @@ fun onInternalMessage(in: InMessage) { } } -/// @notice Deploys a new ContextExecutor at the given AutoDeployAddress. -/// @dev The deploy message carries no body — code and data are embedded in the address. -fun deployBurnMintContextExecutor(executor: AutoDeployAddress): void { +/// @notice Deploys a new ContextExecutor at the given AutoDeployAddress with an Ask message. +/// @notice Deploys a ContextExecutor with an Ask message (empty forwardPayload). +/// The context is already embedded in the CE state from init, so forwardPayload is not needed. +/// The pool awaits the Reply before sending the operational message, ensuring sequential +/// execution (no race condition). +/// @param executor The deterministic CE address to deploy. +/// @param queryId QueryId for correlation between Ask and Reply. +fun deployBurnMintContextExecutorWithAsk( + executor: AutoDeployAddress, + queryId: uint64, +): void { val deploy = createMessage({ bounce: BounceMode.NoBounce, value: BurnMintTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: createEmptyCell().beginParse(), + body: ContextExecutor_Ask { + queryId: queryId, + forwardPayload: null, + done: false, + }, }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Validates a ContextExecutor_Reply by recomputing the expected CE address from +/// the reply fields, then dispatches the operational message based on the context type. +fun onContextExecutorReply( + mutate st: Storage, + sender: address, + msg: ContextExecutor_Reply, +) { + // Validate CE identity: recompute address from reply fields and compare to sender. + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), BurnMintTokenPool_Error.InvalidContextExecutorReply); + + val context = msg.context.load(); + match (context) { + BurnMintTokenPool_BurnContext => { + sendBurnMessage(st, sender, msg.queryId, context); + } + BurnMintTokenPool_MintContext => { + sendMintMessage(st, sender, msg.queryId, context); + } + } +} + +/// @notice Sends the AskToBurn operational message after CE deployment is confirmed. +fun sendBurnMessage( + _st: Storage, + executorAddress: address, + queryId: uint64, + context: BurnMintTokenPool_BurnContext, +) { + val operation = context.forwardPayload.load(); + val prepared = lazy operation.prepared.load(); + val burnMsg = createMessage({ + bounce: BounceMode.RichBounce, + value: BurnMintTokenPool_BURN_VALUE, + dest: context.wallet, + body: AskToBurn { + queryId: queryId, + jettonAmount: prepared.destTokenAmount, + sendExcessesTo: executorAddress, + customPayload: context.toCell(), + }, + }); + burnMsg.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + +/// @notice Sends the MintNewJettons operational message after CE deployment is confirmed. +fun sendMintMessage( + st: Storage, + executorAddress: address, + queryId: uint64, + context: BurnMintTokenPool_MintContext, +) { + val jettonClient = st.poolData.load().jettonClient(); + val operation = context.forwardPayload.load(); + val prepared = lazy operation.prepared.load(); + val mint = createMessage({ + bounce: BounceMode.RichBounce, + value: BurnMintTokenPool_MINT_VALUE, + dest: jettonClient.masterAddress, + body: MintNewJettons { + queryId: queryId, + mintRecipient: operation.requestMsg.load().request.load().transfer.details.load().receiver, + tonAmount: BurnMintTokenPool_MINT_VALUE, + internalTransferMsg: InternalTransferStep { + queryId: queryId, + jettonAmount: prepared.localAmount, + transferInitiator: null, + sendExcessesTo: executorAddress, + forwardTonAmount: 0, + forwardPayload: beginCell().storeBool(true).storeRef(context.toCell()).endCell().beginParse(), + }.toCell(), + }, + }); + mint.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + /// @notice Handles bounced burn/mint messages: refunds rate limit and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { var st = Storage.load(); @@ -136,21 +234,9 @@ fun onLockOrBurnTransferContinue( [jettonClient.masterAddress], ); - deployBurnMintContextExecutor(executor); - - val prepared = lazy fwdPayload.prepared.load(); - val burnMsg = createMessage({ - bounce: BounceMode.RichBounce, - value: BurnMintTokenPool_BURN_VALUE, - dest: sender, - body: AskToBurn { - queryId: requestMsg.queryId, - jettonAmount: prepared.destTokenAmount, - sendExcessesTo: executor.calculateAddress(), - customPayload: context.toCell(), - }, - }); - burnMsg.send(SEND_MODE_PAY_FEES_SEPARATELY); + // Deploy CE with Ask — context is already in CE state from init. + // Reply triggers the operational message (no race condition). + deployBurnMintContextExecutorWithAsk(executor, requestMsg.queryId); return st; } @@ -190,27 +276,12 @@ fun onReleaseOrMintContinue( [recipientWallet], ); - deployBurnMintContextExecutor(executor); - - val mint = createMessage({ - bounce: BounceMode.RichBounce, - value: BurnMintTokenPool_MINT_VALUE, - dest: jettonClient.masterAddress, - body: MintNewJettons { - queryId: msg.queryId, - mintRecipient: details.receiver, - tonAmount: BurnMintTokenPool_MINT_VALUE, - internalTransferMsg: InternalTransferStep { - queryId: msg.queryId, - jettonAmount: prepared.localAmount, - transferInitiator: null, - sendExcessesTo: executor.calculateAddress(), - forwardTonAmount: 0, - forwardPayload: beginCell().storeBool(true).storeRef(context.toCell()).endCell().beginParse(), - }.toCell(), - }, - }); - mint.send(SEND_MODE_PAY_FEES_SEPARATELY); + // Deploy CE with Ask — context is already in CE state from init. + // Reply triggers the operational message (no race condition). + deployBurnMintContextExecutorWithAsk( + executor, + msg.queryId, + ); return st; } diff --git a/contracts/contracts/ccip/pools/burn_mint/errors.tolk b/contracts/contracts/ccip/pools/burn_mint/errors.tolk index 4aff79476..ad3b4ba85 100644 --- a/contracts/contracts/ccip/pools/burn_mint/errors.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/errors.tolk @@ -7,4 +7,5 @@ enum BurnMintTokenPool_Error { UnexpectedBurnBounce = BurnMintTokenPool_FACILITY_ID * 100 UnexpectedMintBounce ContextExecutorUnavailable + InvalidContextExecutorReply } diff --git a/contracts/contracts/ccip/pools/burn_mint/messages.tolk b/contracts/contracts/ccip/pools/burn_mint/messages.tolk index 59792be59..8a02f82a4 100644 --- a/contracts/contracts/ccip/pools/burn_mint/messages.tolk +++ b/contracts/contracts/ccip/pools/burn_mint/messages.tolk @@ -9,6 +9,7 @@ struct (0x39898e4d) BurnMintTokenPool_ClaimMinterAdmin { type BurnMintTokenPool_InMessage = | BurnMintTokenPool_ClaimMinterAdmin + | ContextExecutor_Reply | ContextExecutor_ForwardNotification; type BurnMintTokenPool_OutMessage = diff --git a/contracts/contracts/ccip/pools/lock_release/contract.tolk b/contracts/contracts/ccip/pools/lock_release/contract.tolk index 8429c2594..0b34cfa8a 100644 --- a/contracts/contracts/ccip/pools/lock_release/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release/contract.tolk @@ -47,6 +47,10 @@ fun onInternalMessage(in: InMessage) { val msg = lazy LockReleaseTokenPool_InMessage.fromSlice(in.body); match (msg) { + ContextExecutor_Reply => { + onContextExecutorReply(mutate st, in.senderAddress, msg); + st.store(); + } ContextExecutor_ForwardNotification => { onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); @@ -57,16 +61,70 @@ fun onInternalMessage(in: InMessage) { } } -fun deployLockReleaseContextExecutor(executor: AutoDeployAddress): void { +/// @notice Deploys a new ContextExecutor with an Ask message. +/// The Ask carries the release context in forwardPayload. The pool awaits the Reply +/// before sending the operational transfer message, ensuring sequential execution. +/// @notice Deploys a ContextExecutor with an Ask message (empty forwardPayload). +/// The context is already embedded in the CE state from init, so forwardPayload is not needed. +/// @param executor The deterministic CE address to deploy. +/// @param queryId QueryId for correlation between Ask and Reply. +fun deployLockReleaseContextExecutor( + executor: AutoDeployAddress, + queryId: uint64, +): void { val deploy = createMessage({ bounce: BounceMode.NoBounce, value: LockReleaseTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: createEmptyCell().beginParse(), + body: ContextExecutor_Ask { + queryId: queryId, + forwardPayload: null, + done: false, + }, }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Validates a ContextExecutor_Reply by recomputing the expected CE address from +/// the reply fields, then sends the AskToTransfer operational message. +fun onContextExecutorReply( + mutate st: Storage, + sender: address, + msg: ContextExecutor_Reply, +) { + // Validate CE identity: recompute address from reply fields and compare to sender. + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), Error.InvalidContextExecutorReply); + + val context = msg.context.load(); + val operation = context.forwardPayload.load(); + val prepared = lazy operation.prepared.load(); + val requestIn = lazy operation.requestMsg.load().request.load(); + val details = lazy requestIn.transfer.details.load(); + + val transfer = createMessage({ + bounce: BounceMode.RichBounce, + value: LockReleaseTokenPool_RELEASE_TRANSFER_VALUE, + dest: st.poolData.load().jettonClient().walletAddress(), + body: AskToTransfer { + queryId: msg.queryId, + jettonAmount: prepared.localAmount, + transferRecipient: details.receiver, + sendExcessesTo: sender, + customPayload: context.toCell(), + forwardTonAmount: 0, + forwardPayload: beginCell().storeBool(false).endCell().beginParse(), + }, + }); + transfer.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + /// @notice Handles bounced release messages: refunds rate limit and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { var st = Storage.load(); @@ -116,25 +174,9 @@ fun onReleaseOrMintContinue( [recipientWallet], ); - deployLockReleaseContextExecutor(executor); - - val ownWallet = jettonClient.walletAddress(); - - val transfer = createMessage({ - bounce: BounceMode.RichBounce, - value: LockReleaseTokenPool_RELEASE_TRANSFER_VALUE, - dest: ownWallet, - body: AskToTransfer { - queryId: msg.queryId, - jettonAmount: prepared.localAmount, - transferRecipient: details.receiver, - sendExcessesTo: executor.calculateAddress(), - customPayload: context.toCell(), - forwardTonAmount: 0, - forwardPayload: beginCell().storeBool(false).endCell().beginParse(), - }, - }); - transfer.send(SEND_MODE_PAY_FEES_SEPARATELY); + // Deploy CE with Ask — context is already in CE state from init. + // The operational message (AskToTransfer) is sent after CE confirms. + deployLockReleaseContextExecutor(executor, msg.queryId); return st; } diff --git a/contracts/contracts/ccip/pools/lock_release/errors.tolk b/contracts/contracts/ccip/pools/lock_release/errors.tolk index 4992673e1..22e672a01 100644 --- a/contracts/contracts/ccip/pools/lock_release/errors.tolk +++ b/contracts/contracts/ccip/pools/lock_release/errors.tolk @@ -6,4 +6,5 @@ const LockReleaseTokenPool_FACILITY_ID = 263; // (crc32() % 640) + 10 enum Error { UnexpectedReleaseBounce = LockReleaseTokenPool_FACILITY_ID * 100 ContextExecutorUnavailable + InvalidContextExecutorReply } diff --git a/contracts/contracts/ccip/pools/lock_release/messages.tolk b/contracts/contracts/ccip/pools/lock_release/messages.tolk index b2fd37310..1df54b346 100644 --- a/contracts/contracts/ccip/pools/lock_release/messages.tolk +++ b/contracts/contracts/ccip/pools/lock_release/messages.tolk @@ -4,6 +4,7 @@ import "../../executors/context_executor/messages" import "types" type LockReleaseTokenPool_InMessage = + | ContextExecutor_Reply | ContextExecutor_ForwardNotification; type LockReleaseTokenPool_OutMessage = diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk index f91acd354..64c5218c5 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/contract.tolk @@ -60,6 +60,10 @@ fun onInternalMessage(in: InMessage): void { onReturnExcessesBack(st, in.senderAddress, msg); st.store(); } + ContextExecutor_Reply => { + onContextExecutorReply(mutate st, in.senderAddress, msg); + st.store(); + } ContextExecutor_ForwardNotification => { onContextExecutorForwardNotification(mutate st, in.senderAddress, msg); st.store(); @@ -70,16 +74,76 @@ fun onInternalMessage(in: InMessage): void { } } -fun deployLockReleaseLockboxContextExecutor(executor: AutoDeployAddress): void { +/// @notice Deploys a new ContextExecutor with an Ask message. +/// The Ask carries the release context in forwardPayload. The pool awaits the Reply +/// before sending the operational message to the lockbox, ensuring sequential execution. +/// @notice Deploys a ContextExecutor with an Ask message (empty forwardPayload). +/// The context is already embedded in the CE state from init, so forwardPayload is not needed. +/// @param executor The deterministic CE address to deploy. +/// @param queryId QueryId for correlation between Ask and Reply. +fun deployLockReleaseLockboxContextExecutor( + executor: AutoDeployAddress, + queryId: uint64, +): void { val deploy = createMessage({ bounce: BounceMode.NoBounce, value: LockReleaseLockboxTokenPool_CONTEXT_EXECUTOR_DEPLOY_VALUE, dest: executor, - body: createEmptyCell().beginParse(), + body: ContextExecutor_Ask { + queryId: queryId, + forwardPayload: null, + done: false, + }, }); deploy.send(SEND_MODE_PAY_FEES_SEPARATELY); } +/// @notice Validates a ContextExecutor_Reply by recomputing the expected CE address from +/// the reply fields, then sends the JettonLockBox_Withdraw operational message. +fun onContextExecutorReply( + mutate st: Storage, + sender: address, + msg: ContextExecutor_Reply, +) { + // Validate CE identity: recompute address from reply fields and compare to sender. + val expectedExecutor = ContextExecutor.getAddress( + st.contextExecutorCode, + msg.id, + contract.getAddress(), + msg.context, + msg.forwardFrom, + ); + assert(sender == expectedExecutor.calculateAddress(), LockReleaseLockBoxTokenPool_Error.InvalidContextExecutorReply); + + val context = msg.context.load(); + val operation = context.forwardPayload.load(); + val prepared = lazy operation.prepared.load(); + val requestIn = lazy operation.requestMsg.load().request.load(); + val details = lazy requestIn.transfer.details.load(); + + // Return excesses are the successful withdrawal completion signal. + val extra = JettonLockBox_WithdrawExtra { + sendExcessesTo: sender, + failureContext: context.toCell(), + }.toCell(); + + // Send JettonLockBox_Withdraw to the lockbox. + val withdraw = createMessage({ + bounce: BounceMode.RichBounce, + value: ton("0.1"), + dest: st.lockbox, + body: JettonLockBox_Withdraw { + queryId: msg.queryId, + token: st.poolData.load().jettonClient().masterAddress, + remoteChainSelector: details.remoteChainSelector, + amount: prepared.localAmount, + recipientWallet: details.receiver, + extra, + }, + }); + withdraw.send(SEND_MODE_PAY_FEES_SEPARATELY); +} + /// @notice Handles bounced lock (deposit) messages: returns jettons and sends failure reply. fun onBouncedMessage(in: InMessageBounced) { val msg = lazy LockReleaseLockboxTokenPool_BouncedMessage.fromSlice(in.bouncedBody.skipBouncedPrefix()); @@ -248,32 +312,10 @@ fun onReleaseOrMintContinue( context.toCell(), [recipientWallet], ); - deployLockReleaseLockboxContextExecutor(executor); - - // Return excesses are the successful withdrawal completion signal. - val extra = JettonLockBox_WithdrawExtra { - sendExcessesTo: executor.calculateAddress(), - failureContext: context.toCell(), - }.toCell(); - // Send JettonLockBox_Withdraw to the lockbox. - // The lockbox passes recipientWallet as transferRecipient in AskToTransfer, - // and the CCT JettonWallet computes the wallet from it. So we pass the raw - // receiver (owner) address, not the pre-computed wallet address. - val withdraw = createMessage({ - bounce: BounceMode.RichBounce, - value: ton("0.1"), - dest: st.lockbox, - body: JettonLockBox_Withdraw { - queryId: msg.queryId, - token: jettonClient.masterAddress, - remoteChainSelector: details.remoteChainSelector, - amount: prepared.localAmount, - recipientWallet: details.receiver, - extra, - }, - }); - withdraw.send(SEND_MODE_PAY_FEES_SEPARATELY); + // Deploy CE with Ask — context is already in CE state from init. + // The operational message (JettonLockBox_Withdraw) is sent after CE confirms. + deployLockReleaseLockboxContextExecutor(executor, msg.queryId); return st; } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk index 487f0db04..a7b36a609 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/errors.tolk @@ -9,4 +9,5 @@ enum LockReleaseLockBoxTokenPool_Error { UnexpectedLockboxConfirmationSender UnexpectedLockBounce + InvalidContextExecutorReply } diff --git a/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk b/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk index 6ae3fd7be..ff6021c75 100644 --- a/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk +++ b/contracts/contracts/ccip/pools/lock_release_lockbox/messages.tolk @@ -9,6 +9,7 @@ type LockReleaseLockboxTokenPool_InMessage = | ReturnExcessesBack | JettonLockBox_Deposited | JettonLockBox_WithdrawFailed + | ContextExecutor_Reply | ContextExecutor_ForwardNotification; type LockReleaseLockboxTokenPool_OutMessage = diff --git a/contracts/wrappers/gen/ccip/ContextExecutor.ts b/contracts/wrappers/gen/ccip/ContextExecutor.ts index b35b5706b..294fc6d8d 100644 --- a/contracts/wrappers/gen/ccip/ContextExecutor.ts +++ b/contracts/wrappers/gen/ccip/ContextExecutor.ts @@ -258,14 +258,14 @@ export const ContextExecutor_Set = { /** > struct (0xcad4d1d0) ContextExecutor_Ask { > queryId: uint64 - > forwardPayload: cell + > forwardPayload: cell? > done: bool > } */ export interface ContextExecutor_Ask { readonly $: 'ContextExecutor_Ask' queryId: uint64 - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean } @@ -274,7 +274,7 @@ export const ContextExecutor_Ask = { create(args: { queryId?: uint64 - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean }): ContextExecutor_Ask { return { @@ -288,14 +288,16 @@ export const ContextExecutor_Ask = { return { $: 'ContextExecutor_Ask', queryId: s.loadUintBig(64), - forwardPayload: s.loadRef(), + forwardPayload: s.loadBoolean() ? s.loadRef() : null, done: s.loadBoolean(), } }, store(self: ContextExecutor_Ask, b: c.Builder): void { b.storeUint(0xcad4d1d0, 32); b.storeUint(self.queryId, 64); - b.storeRef(self.forwardPayload); + storeTolkNullable(self.forwardPayload, b, + (v,b) => b.storeRef(v) + ); b.storeBit(self.done); }, toCell(self: ContextExecutor_Ask): c.Cell { @@ -309,7 +311,7 @@ export const ContextExecutor_Ask = { > id: uint64 > context: Cell > forwardFrom: array
- > forwardPayload: cell + > forwardPayload: cell? > done: bool > } */ @@ -319,7 +321,7 @@ export interface ContextExecutor_Reply { id: uint64 context: T forwardFrom: array - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean } @@ -331,7 +333,7 @@ export const ContextExecutor_Reply = { id: uint64 context: T forwardFrom: array - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean }): ContextExecutor_Reply { return { @@ -506,7 +508,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class ContextExecutor implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECFAEAA5UAART/APSkE/S88sgLAQIBYgIDAgLOBAUCASAMDQIBIAYHAElO2i7ftsIjJwIW+Ikly5jhAhpFIzb4EkxwWVXwR/2zHg6F8EcIAas+JHyQO1E0NM/+kjUbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidH4kviX+Jj4k3D4OviU+JUqyM7J8AHjAl8EhA8BxwDy9IAgC9wg0NcsIicw92TjAtcsJlamjoSOZmxh0z/U1woAyM+ST5bvFhPLPyfPCz8lzxQkb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU5BvgVj6UgGk5AHJAqHkMDECywf0AMwhzwoAycjPhQgT+lJxzwtuEszJAYMGgEDjBPsAf+CAJCgB2NALIyz/6Uswhb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU2BvgVj6UgGk5AHJAqHkMDEzzwsH9ADJ7VQB/jhfBjKCAKpQXccF8vTTP9RvAAHTB/QFkyBus44Q0PQEmvpIUERvjCPHABTmMOgwIW+IuvKJcMjLP8nIz5JPlu8WFMs/Js8LPyLPFCFviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTYG+BWPpSAaTkAckCoeQwMQLLB/QAE8zPgckLAPYwVHqYU6nwAo5rbwAHyPpSUAb6AhT0AFj6Ass/yx/MycjPkVbQSuYmzws/JM8UI2+Ic21UciGpBo4bAcj0AFMhtghRIqEimVOAb4FY+lIBpOQByQKh5DAxNc8LBxP0ABLMycjPhQhSQPpScc8LbszJgED7AH/gbGHQxwAAJsjPhQgU+lJxzwtuE8zJgED7AH8CASAODwBtvOyHaiaGmf/SQY6hi3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjAICchARAgEgEhMAXqlfjQjbGluay5jaGFpbi50b24uY2NpcC5Db250ZXh0RXhlY3V0b3KCLUwLjEuMIAGyoWu1E0NM/MfpIMdQxbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidEAbbSjvaiaGmfmP0kahi3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjAAbbQBfaiaGmfmP0kGOo3gADpg/oCSZC3WccJAOh6Ak19JCgqt8YSY4AK8xgA9BiRN8QZgV15ROjA='); + static CodeCell = c.Cell.fromBase64('te6ccgECFQEAA5kAART/APSkE/S88sgLAQIBYgIDAgLOBAUCASANDgIBIAYHAElO2i7ftsIjJwIW+Ikly5jhAhpFIzb4EkxwWVXwR/2zHg6F8EcIAas+JHyQO1E0NM/+kjUbwAB0wf0BJMhbrOOEgHQ9ASa+khQVW+MJMcAFeYwAegxIm+IWLryidH4kviX+Jj4k3D4OviU+JUqyM7J8AHjAl8EhA8BxwDy9IAgDRwg0NcsIicw92TjAtcsJlamjoTjAjBUephTqfAC4wJsYdDHAIAkKCwB2NALIyz/6Uswhb4hzbVRyIakGjhsByPQAUyG2CFEioSKZU2BvgVj6UgGk5AHJAqHkMDEzzwsH9ADJ7VQB/jhfBjKCAKpQXccF8vTTP9RvAAHTB/QFkyBus44Q0PQEmvpIUERvjCPHABTmMOgwIW+IuvKJcMjLP8nIz5JPlu8WFMs/Js8LPyLPFCFviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTYG+BWPpSAaTkAckCoeQwMQLLB/QAE/QAz4EMANBsYdM/9ATXCgDIz5JPlu8WE8s/J88LPyXPFCRviHNtVHIhqQaOGwHI9ABTIbYIUSKhIplTkG+BWPpSAaTkAckCoeQwMQLLB/QA9AAhzwoAycjPhQgT+lJxzwtuEszJAYMGgEDjBPsAfwDWbwAHyPpSUAb6AhT0AFj6Ass/yx/MycjPkVbQSuYmzws/JM8UI2+Ic21UciGpBo4bAcj0AFMhtghRIqEimVOAb4FY+lIBpOQByQKh5DAxNc8LBxP0ABLMycjPhQhSQPpScc8LbszJgED7AH8AKMnIz4UIFPpScc8LbhPMyYBA+wB/AgEgDxAAbbzsh2omhpn/0kGOoYt4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTowCAnIREgIBIBMUAF6pX40I2xpbmsuY2hhaW4udG9uLmNjaXAuQ29udGV4dEV4ZWN1dG9ygi1MC4xLjCABsqFrtRNDTPzH6SDHUMW8AAdMH9ASTIW6zjhIB0PQEmvpIUFVvjCTHABXmMAHoMSJviFi68onRAG20o72omhpn5j9JGoYt4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTowAG20AX2omhpn5j9JBjqN4AA6YP6AkmQt1nHCQDoegJNfSQoKrfGEmOACvMYAPQYkTfEGYFdeUTow'); static Errors = { 'ContextExecutor_Error.OnlyCallableByOwner': 43600, @@ -566,7 +568,7 @@ export class ContextExecutor implements c.Contract { static createCellOfContextExecutorAsk(body: { queryId?: uint64 - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean }) { return ContextExecutor_Ask.toCell(ContextExecutor_Ask.create(body)); @@ -603,7 +605,7 @@ export class ContextExecutor implements c.Contract { async sendContextExecutorAsk(provider: ContractProvider, via: Sender, msgValue: coins, body: { queryId?: uint64 - forwardPayload: c.Cell + forwardPayload: c.Cell | null done: boolean }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { diff --git a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts index 607457cad..bc84a36e8 100644 --- a/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/BurnMintTokenPool.ts @@ -920,6 +920,95 @@ export const CrossChainAddress = { } } +/** + > struct (0xcad4d1d0) ContextExecutor_Ask { + > queryId: uint64 + > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Ask { + readonly $: 'ContextExecutor_Ask' + queryId: uint64 + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Ask = { + PREFIX: 0xcad4d1d0, + + create(args: { + queryId?: uint64 + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Ask { + return { + $: 'ContextExecutor_Ask', + ...args, + queryId: args.queryId ?? 0n + } + }, + fromSlice(s: c.Slice): ContextExecutor_Ask { + loadAndCheckPrefix32(s, 0xcad4d1d0, 'ContextExecutor_Ask'); + return { + $: 'ContextExecutor_Ask', + queryId: s.loadUintBig(64), + forwardPayload: s.loadBoolean() ? s.loadRef() : null, + done: s.loadBoolean(), + } + }, + store(self: ContextExecutor_Ask, b: c.Builder): void { + b.storeUint(0xcad4d1d0, 32); + b.storeUint(self.queryId, 64); + storeTolkNullable(self.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(self.done); + }, + toCell(self: ContextExecutor_Ask): c.Cell { + return makeCellFrom(self, ContextExecutor_Ask.store); + } +} + +/** + > struct (0x93e5bbc5) ContextExecutor_Reply { + > queryId: uint64 + > id: uint64 + > context: Cell + > forwardFrom: array
+ > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Reply { + readonly $: 'ContextExecutor_Reply' + queryId: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Reply = { + PREFIX: 0x93e5bbc5, + + create(args: { + queryId?: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Reply { + return { + $: 'ContextExecutor_Reply', + ...args, + queryId: args.queryId ?? 0n + } + }, +} + /** > struct (0x55b412b9) ContextExecutor_ForwardNotification { > id: uint64 @@ -4872,7 +4961,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class BurnMintTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4900,6 +4989,7 @@ export class BurnMintTokenPool implements c.Contract { 'BurnMintTokenPool_Error.UnexpectedBurnBounce': 41200, 'BurnMintTokenPool_Error.UnexpectedMintBounce': 41201, 'BurnMintTokenPool_Error.ContextExecutorUnavailable': 41202, + 'BurnMintTokenPool_Error.InvalidContextExecutorReply': 41203, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, @@ -5086,6 +5176,29 @@ export class BurnMintTokenPool implements c.Contract { return BurnMintTokenPool_ClaimMinterAdmin.toCell(BurnMintTokenPool_ClaimMinterAdmin.create(body)); } + static createCellOfContextExecutorReplyBurnMintTokenPoolContextExecutorContext_(body: { + queryId?: uint64 + id: uint64 + context: BurnMintTokenPool_ContextExecutorContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }) { + return makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, BurnMintTokenPool_ContextExecutorContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ); + } + static createCellOfContextExecutorForwardNotificationBurnMintTokenPoolContextExecutorContext_(body: { id: uint64 context: BurnMintTokenPool_ContextExecutorContext @@ -5332,6 +5445,33 @@ export class BurnMintTokenPool implements c.Contract { }); } + async sendContextExecutorReplyBurnMintTokenPoolContextExecutorContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryId?: uint64 + id: uint64 + context: BurnMintTokenPool_ContextExecutorContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, BurnMintTokenPool_ContextExecutorContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ), + ...extraOptions + }); + } + async sendContextExecutorForwardNotificationBurnMintTokenPoolContextExecutorContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { id: uint64 context: BurnMintTokenPool_ContextExecutorContext diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts index 8f2f404a5..7fc5d1fba 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseLockboxTokenPool.ts @@ -627,6 +627,95 @@ export const CrossChainAddress = { } } +/** + > struct (0xcad4d1d0) ContextExecutor_Ask { + > queryId: uint64 + > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Ask { + readonly $: 'ContextExecutor_Ask' + queryId: uint64 + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Ask = { + PREFIX: 0xcad4d1d0, + + create(args: { + queryId?: uint64 + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Ask { + return { + $: 'ContextExecutor_Ask', + ...args, + queryId: args.queryId ?? 0n + } + }, + fromSlice(s: c.Slice): ContextExecutor_Ask { + loadAndCheckPrefix32(s, 0xcad4d1d0, 'ContextExecutor_Ask'); + return { + $: 'ContextExecutor_Ask', + queryId: s.loadUintBig(64), + forwardPayload: s.loadBoolean() ? s.loadRef() : null, + done: s.loadBoolean(), + } + }, + store(self: ContextExecutor_Ask, b: c.Builder): void { + b.storeUint(0xcad4d1d0, 32); + b.storeUint(self.queryId, 64); + storeTolkNullable(self.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(self.done); + }, + toCell(self: ContextExecutor_Ask): c.Cell { + return makeCellFrom(self, ContextExecutor_Ask.store); + } +} + +/** + > struct (0x93e5bbc5) ContextExecutor_Reply { + > queryId: uint64 + > id: uint64 + > context: Cell + > forwardFrom: array
+ > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Reply { + readonly $: 'ContextExecutor_Reply' + queryId: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Reply = { + PREFIX: 0x93e5bbc5, + + create(args: { + queryId?: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Reply { + return { + $: 'ContextExecutor_Reply', + ...args, + queryId: args.queryId ?? 0n + } + }, +} + /** > struct (0x55b412b9) ContextExecutor_ForwardNotification { > id: uint64 @@ -4823,7 +4912,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseLockboxTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4852,6 +4941,7 @@ export class LockReleaseLockboxTokenPool implements c.Contract { 'LockReleaseLockBoxTokenPool_Error.LockboxNotConfigured': 48701, 'LockReleaseLockBoxTokenPool_Error.UnexpectedLockboxConfirmationSender': 48702, 'LockReleaseLockBoxTokenPool_Error.UnexpectedLockBounce': 48703, + 'LockReleaseLockBoxTokenPool_Error.InvalidContextExecutorReply': 48704, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, 'Ownable2Step_Error.MustBeProposedOwner': 49802, @@ -5059,6 +5149,29 @@ export class LockReleaseLockboxTokenPool implements c.Contract { return JettonLockBox_WithdrawFailed.toCell(JettonLockBox_WithdrawFailed.create(body)); } + static createCellOfContextExecutorReplyLockReleaseLockboxTokenPoolReleaseContext_(body: { + queryId?: uint64 + id: uint64 + context: LockReleaseLockboxTokenPool_ReleaseContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }) { + return makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseLockboxTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ); + } + static createCellOfContextExecutorForwardNotificationLockReleaseLockboxTokenPoolReleaseContext_(body: { id: uint64 context: LockReleaseLockboxTokenPool_ReleaseContext @@ -5333,6 +5446,33 @@ export class LockReleaseLockboxTokenPool implements c.Contract { }); } + async sendContextExecutorReplyLockReleaseLockboxTokenPoolReleaseContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryId?: uint64 + id: uint64 + context: LockReleaseLockboxTokenPool_ReleaseContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseLockboxTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ), + ...extraOptions + }); + } + async sendContextExecutorForwardNotificationLockReleaseLockboxTokenPoolReleaseContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { id: uint64 context: LockReleaseLockboxTokenPool_ReleaseContext diff --git a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts index e4a0d6486..dfa39b2f8 100644 --- a/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts +++ b/contracts/wrappers/gen/ccip/pools/LockReleaseTokenPool.ts @@ -711,6 +711,95 @@ export const CrossChainAddress = { } } +/** + > struct (0xcad4d1d0) ContextExecutor_Ask { + > queryId: uint64 + > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Ask { + readonly $: 'ContextExecutor_Ask' + queryId: uint64 + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Ask = { + PREFIX: 0xcad4d1d0, + + create(args: { + queryId?: uint64 + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Ask { + return { + $: 'ContextExecutor_Ask', + ...args, + queryId: args.queryId ?? 0n + } + }, + fromSlice(s: c.Slice): ContextExecutor_Ask { + loadAndCheckPrefix32(s, 0xcad4d1d0, 'ContextExecutor_Ask'); + return { + $: 'ContextExecutor_Ask', + queryId: s.loadUintBig(64), + forwardPayload: s.loadBoolean() ? s.loadRef() : null, + done: s.loadBoolean(), + } + }, + store(self: ContextExecutor_Ask, b: c.Builder): void { + b.storeUint(0xcad4d1d0, 32); + b.storeUint(self.queryId, 64); + storeTolkNullable(self.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(self.done); + }, + toCell(self: ContextExecutor_Ask): c.Cell { + return makeCellFrom(self, ContextExecutor_Ask.store); + } +} + +/** + > struct (0x93e5bbc5) ContextExecutor_Reply { + > queryId: uint64 + > id: uint64 + > context: Cell + > forwardFrom: array
+ > forwardPayload: cell? + > done: bool + > } + */ +export interface ContextExecutor_Reply { + readonly $: 'ContextExecutor_Reply' + queryId: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean +} + +export const ContextExecutor_Reply = { + PREFIX: 0x93e5bbc5, + + create(args: { + queryId?: uint64 + id: uint64 + context: T + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }): ContextExecutor_Reply { + return { + $: 'ContextExecutor_Reply', + ...args, + queryId: args.queryId ?? 0n + } + }, +} + /** > struct (0x55b412b9) ContextExecutor_ForwardNotification { > id: uint64 @@ -4340,40 +4429,6 @@ export const TokenPool_RampAccessUpdated = { } } -/** - > type LockReleaseTokenPool_InMessage = ContextExecutor_ForwardNotification - */ -export type LockReleaseTokenPool_InMessage = ContextExecutor_ForwardNotification - -export const LockReleaseTokenPool_InMessage = { - fromSlice(s: c.Slice): LockReleaseTokenPool_InMessage { - return (() => { - loadAndCheckPrefix32(s, 0x55b412b9, 'ContextExecutor_ForwardNotification'); - return { - $: 'ContextExecutor_ForwardNotification', - id: s.loadUintBig(64), - context: loadCellRef(s, LockReleaseTokenPool_ReleaseContext.fromSlice), - forwardFrom: loadArrayOf(s, - (s) => s.loadAddress() - ), - message: loadCellRef(s, ContextExecutor_InMessageForward.fromSlice), - } - })(); - }, - store(self: LockReleaseTokenPool_InMessage, b: c.Builder): void { - b.storeUint(0x55b412b9, 32); - b.storeUint(self.id, 64); - storeCellRef(self.context, b, LockReleaseTokenPool_ReleaseContext.store); - storeArrayOf(self.forwardFrom, b, - (v,b) => b.storeAddress(v) - ); - storeCellRef(self.message, b, ContextExecutor_InMessageForward.store); - }, - toCell(self: LockReleaseTokenPool_InMessage): c.Cell { - return makeCellFrom(self, LockReleaseTokenPool_InMessage.store); - } -} - /** > struct (0xed696f9b) LockReleaseTokenPool_ReleaseContext { > forwardPayload: Cell @@ -4589,7 +4644,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class LockReleaseTokenPool implements c.Contract { - static CodeCell = c.Cell.fromBase64('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'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -4615,6 +4670,7 @@ export class LockReleaseTokenPool implements c.Contract { 'RateLimiter_Error.BucketOverfilled': 26300, 'Error.ContextExecutorUnavailable': 26301, 'RateLimiter_Error.TokenMaxCapacityExceeded': 26301, + 'Error.InvalidContextExecutorReply': 26302, 'RateLimiter_Error.TokenRateLimitReached': 26302, 'Ownable2Step_Error.OnlyCallableByOwner': 49800, 'Ownable2Step_Error.CannotTransferToSelf': 49801, @@ -4796,8 +4852,44 @@ export class LockReleaseTokenPool implements c.Contract { return TokenPool_SetCursedSubjects.toCell(TokenPool_SetCursedSubjects.create(body)); } - static createCellOfLockReleaseTokenPoolInMessage(body: LockReleaseTokenPool_InMessage) { - return LockReleaseTokenPool_InMessage.toCell(body); + static createCellOfContextExecutorReplyLockReleaseTokenPoolReleaseContext_(body: { + queryId?: uint64 + id: uint64 + context: LockReleaseTokenPool_ReleaseContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }) { + return makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ); + } + + static createCellOfContextExecutorForwardNotificationLockReleaseTokenPoolReleaseContext_(body: { + id: uint64 + context: LockReleaseTokenPool_ReleaseContext + forwardFrom: array + message: ContextExecutor_InMessageForward + }) { + return makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ); } async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { @@ -5019,10 +5111,50 @@ export class LockReleaseTokenPool implements c.Contract { }); } - async sendLockReleaseTokenPoolInMessage(provider: ContractProvider, via: Sender, msgValue: coins, body: LockReleaseTokenPool_InMessage, extraOptions?: ExtraSendOptions) { + async sendContextExecutorReplyLockReleaseTokenPoolReleaseContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryId?: uint64 + id: uint64 + context: LockReleaseTokenPool_ReleaseContext + forwardFrom: array + forwardPayload: c.Cell | null + done: boolean + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: makeCellFrom>(ContextExecutor_Reply.create(body), + (v,b) => { b.storeUint(0x93e5bbc5, 32); + b.storeUint(v.queryId, 64); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeTolkNullable(v.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + b.storeBit(v.done); } + ), + ...extraOptions + }); + } + + async sendContextExecutorForwardNotificationLockReleaseTokenPoolReleaseContext_(provider: ContractProvider, via: Sender, msgValue: coins, body: { + id: uint64 + context: LockReleaseTokenPool_ReleaseContext + forwardFrom: array + message: ContextExecutor_InMessageForward + }, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, - body: LockReleaseTokenPool_InMessage.toCell(body), + body: makeCellFrom>(ContextExecutor_ForwardNotification.create(body), + (v,b) => { b.storeUint(0x55b412b9, 32); + b.storeUint(v.id, 64); + storeCellRef(v.context, b, LockReleaseTokenPool_ReleaseContext.store); + storeArrayOf(v.forwardFrom, b, + (v,b) => b.storeAddress(v) + ); + storeCellRef(v.message, b, ContextExecutor_InMessageForward.store); } + ), ...extraOptions }); } From 20d701266cd0925b7807f753e9475f9ad4a629f1 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Wed, 5 Aug 2026 14:32:54 +0200 Subject: [PATCH 10/11] Fix golangci-lint --- pkg/ccip/bindings/tokenpool/burnmint/types.go | 2 +- pkg/ccip/bindings/tokenpool/lockrelease/types.go | 2 +- pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/ccip/bindings/tokenpool/burnmint/types.go b/pkg/ccip/bindings/tokenpool/burnmint/types.go index 725d01cff..4fff6ffb0 100644 --- a/pkg/ccip/bindings/tokenpool/burnmint/types.go +++ b/pkg/ccip/bindings/tokenpool/burnmint/types.go @@ -61,7 +61,7 @@ type ReturnExcessesBack struct { type Storage struct { PoolData tokenpool.Storage `tlb:"^"` ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment - ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses + ContextExecutorNextID uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses } // --- Exit Codes --- diff --git a/pkg/ccip/bindings/tokenpool/lockrelease/types.go b/pkg/ccip/bindings/tokenpool/lockrelease/types.go index 82133bf45..22813003c 100644 --- a/pkg/ccip/bindings/tokenpool/lockrelease/types.go +++ b/pkg/ccip/bindings/tokenpool/lockrelease/types.go @@ -47,7 +47,7 @@ type ReturnExcessesBack struct { type Storage struct { PoolData tokenpool.Storage `tlb:"^"` ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment - ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses + ContextExecutorNextID uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses } // --- Exit Codes --- diff --git a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go index 167df61c1..5e91b6d48 100644 --- a/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go +++ b/pkg/ccip/bindings/tokenpool/lockreleaselockbox/types.go @@ -50,7 +50,7 @@ type Storage struct { PoolData tokenpool.Storage `tlb:"^"` Lockbox *address.Address `tlb:"addr"` // JettonLockBox address ContextExecutorCode *cell.Cell `tlb:"^"` // Code cell for ContextExecutor deployment - ContextExecutorNextId uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses + ContextExecutorNextID uint64 `tlb:"## 64"` // Monotonically increasing ID for deterministic executor addresses } // --- Exit Codes --- From fe725224fab71d6c105d4af274d00cdf09ee4cb8 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Wed, 5 Aug 2026 14:35:35 +0200 Subject: [PATCH 11/11] Remove local review --- .../review-lock-release-lockbox-tokenpool.md | 310 ------------------ 1 file changed, 310 deletions(-) delete mode 100644 contracts/reviews/review-lock-release-lockbox-tokenpool.md diff --git a/contracts/reviews/review-lock-release-lockbox-tokenpool.md b/contracts/reviews/review-lock-release-lockbox-tokenpool.md deleted file mode 100644 index 6dbabc602..000000000 --- a/contracts/reviews/review-lock-release-lockbox-tokenpool.md +++ /dev/null @@ -1,310 +0,0 @@ -# LockReleaseLockboxTokenPool - Pre-Publish Code Review - -## Executive Summary - -This is a deep, pre-publish review comparing the TON `LockReleaseLockboxTokenPool` implementation against the EVM `LockReleaseTokenPool`. Overall the architecture is sound, but there are several items worth addressing before publish. - ---- - -## Architecture Comparison: TON vs EVM - -### EVM (Synchronous) - -``` -User → Router → LockReleaseTokenPool._lockOrBurn() - └── i_lockBox.deposit() [synchronous, atomic] - └── emit LockedOrBurned() [same tx] -``` - -### TON (Asynchronous Message Chains) - -``` -User → Pool wallet → TransferNotificationForRecipient → pool - └── onLockOrBurnTransferContinue() [store pending lock] - └── AskToTransfer [to pool wallet] - └── TransferNotificationForRecipient [to lockbox wallet] - └── JettonLockBox_Deposited [to pool] - └── onLockDepositCompleted() [finalize + emit event] -``` - -**Key difference**: TON emits `TokenPool_LockedOrBurned` **asynchronously** (when lockbox callback arrives), while EVM emits `LockedOrBurned` **synchronously** (in same call). This is a known TON architectural constraint, not a bug. - ---- - -## 🔴 Critical Issues (Fix Before Publish) - -### 1. Silent Failure on Bounced Lock Transfers - -**Location**: `contract.tolk:193-203` - `onLockTransferBounced` - -```tolk -fun onLockTransferBounced(...) { - val pendingEntry = st.pendingLocks.get(msg.queryId); - if (!pendingEntry.isFound) { return; } - val pending = pendingEntry.loadValue().load(); - st.pendingLocks.delete(msg.queryId); - // TODO: try to best-effort return jettons or emit error event -} -``` - -**Problem**: When the AskToTransfer bounces (lockbox wallet can't accept, insufficient TON, etc.): - -- Pending lock is silently deleted -- NO error event emitted -- NO reply sent to Router/OnRamp -- Jettons are already transferred to the pool's wallet - they're "stuck" in the pool but the lock never completed -- Off-chain relayer will see the lock never finalized and may retry, but the jettons are already on the pool - -**EVM comparison**: EVM is atomic - if the deposit fails, the entire tx reverts. No stuck state possible. - -**Fix options**: - -1. Emit a `TokenPool_LockOrBurnFailed` event with error code -2. Send `TokenPool_ReleaseOrMintFailure` to replyTo (matching release flow pattern) -3. Consider trying to bounce jettons back (best-effort return) - -### 2. Hardcoded TON Values in Messages - -**Location**: `contract.tolk:157, 244` - `onLockOrBurnTransferContinue` and `onReleaseOrMintContinue` - -```tolk -val transfer = createMessage({ - value: ton("0.1"), // Hardcoded! - ... -}); -``` - -**Problem**: `ton("0.1")` is hardcoded for both: - -- Lock flow: AskToTransfer to jetton wallet -- Release flow: JettonLockBox_Withdraw to lockbox - -This doesn't account for: - -- Gas price fluctuations -- Complex forward payloads that need more gas -- Future TON fee changes - -**EVM comparison**: EVM uses `msg.value` from the caller - gas is explicit. - -**Recommendation**: Consider making this configurable, or at minimum add a comment explaining why 0.1 TON is sufficient. - -### 3. Incorrect Error Code in WithdrawFailed Handler - -**Location**: `contract.tolk:287` - `onReleaseWithdrawFailed` - -```tolk -body: TokenPool_ReleaseOrMintFailure { - queryId: msg.queryId, - errorCode: TokenPool_Error.UnsupportedOperation as uint16, -}, -``` - -**Problem**: Uses `TokenPool_Error.UnsupportedOperation` as the error code when the lockbox withdraw failed. This is misleading - `UnsupportedOperation` is for when the pool type can't handle a request, not when the lockbox reports a failure. - -**Recommendation**: Use a more appropriate error code. Consider adding `TokenPool_Error.LockboxWithdrawFailed` to the error enum. - ---- - -## 🟡 Medium Priority Issues - -### 4. No Validation of Lockbox Token Support - -**Location**: Constructor / deployment - -**Problem**: EVM's `LockReleaseTokenPool.sol` validates in constructor: - -```solidity -if (!lockBoxContract.isTokenSupported(address(token))) { - revert InvalidToken(address(token)); -} -``` - -TON's `LockReleaseLockboxTokenPool` does NOT validate that the lockbox supports the token at deployment time. A misconfigured pool could be deployed with an unsupported token. - -**Recommendation**: Add validation during deployment/setup. - -### 5. QueryId Collision Window - -**Problem**: `pendingLocks` and `pendingReleases` are maps keyed by `uint64` queryId. While 64-bit gives a large space, there's no protection against intentional queryId reuse (e.g., if the Router sends duplicate requests with the same queryId). - -**Current mitigation**: Duplicate rejection tests pass (exit codes 48700, 48702). - -**Recommendation**: Consider adding TTL or cleanup for stale pending entries. A pending lock that never gets `JettonLockBox_Deposited` callback could block that queryId forever. - -### 6. `transferInitiator` Trust Model - -**Location**: `token_pool.tolk:834-836` - -```tolk -// TODO: can we trust (any) JettonWallet forward payloads? -// assert(request.amount == msg.jettonAmount, TokenPool_Error.AmountMismatch); - -if (msg.transferInitiator != null) { - self.onLockOrBurnTransferContinue(sender, fwdp); - return; -} -``` - -**Problem**: The pool trusts ANY transfer with `transferInitiator != null`. This means any JettonWallet can forward a crafted `TokenPool_LockOrBurnForwardPayload` to the pool. The pool processes it as a valid lock request. - -**Impact**: While the lockbox validates the operator role, a malicious wallet could: - -1. Send jettons with crafted forward payload -2. Pool forwards to lockbox -3. Lockbox rejects (operator check) -4. Jettons return to pool -5. Pool has no way to track this "bounce" state - -**Recommendation**: At minimum, add validation that `forwardPayload.amount == msg.jettonAmount` before processing. - -### 7. No Test for `onLockDepositCompleted` Path - -**Location**: Full lock flow test - -**Problem**: The test explicitly states: - -> "In the sandbox, the lockbox wallet is a standard jetton wallet (not JettonLockBox contract). It returns excesses but does NOT send JettonLockBox_Deposited back to finalize." - -This means the `onLockDepositCompleted` function is **never tested end-to-end**. The event emission path (`TokenPool_LockedOrBurned`) is untested. - -**Recommendation**: Consider integrating the actual JettonLockBox contract in the full lock flow test (not just the wallet). This may require the JettonLockBox to be deployed as a real contract in the sandbox. - -### 8. Redundant Comment in `onReturnExcessesBack` - -**Location**: `contract.tolk:270, 313` - -The comment block appears **twice** (lines 270-275 and 313-318): - -```tolk -/* --- ReturnExcessesBack handler --- */ -/* - * For lock flow: just accept TON, do nothing. Completion comes from JettonLockBox_Deposited. - * For release flow: this IS the completion signal. Restore context, finalize release flow. - */ -``` - -**Recommendation**: Remove the duplicate. - ---- - -## 🟢 Low Priority / Polish - -### 9. Test: "should return jettons for transfers without transferInitiator" is a Comment-Only Test - -**Location**: Test file - -```typescript -it('should return jettons for transfers without transferInitiator (direct user transfers)', async () => { - // ... 8 lines of comments ... - // No assertions! -}) -``` - -**Recommendation**: Either: - -- Remove this test (the logic is in TokenPool library, not pool-specific) -- Or make it an actual test that verifies the behavior - -### 10. Test File Uses `require('zlib')` Inline - -**Location**: Test file, line 195 - -```typescript -const { crc32 } = require('zlib') -``` - -**Recommendation**: Move to top-level imports for consistency. - -### 11. Unused `lockboxOperator` Treasury Account - -**Location**: Test file - -```typescript -let lockboxOperator: SandboxContract -``` - -Declared but never used. - -### 12. Unused `pool` Variable Alias - -**Location**: Test file - -```typescript -let pool: SandboxContract -``` - -Used for `runTokenPoolBehaviorTests` but not in pool-specific tests. Consider documenting this alias pattern. - -### 13. Consider Adding `getPendingLocksCount` and `getPendingReleasesCount` Getters - -**Current state**: Only `getHasPendingLock(queryId)` and `getHasPendingRelease(queryId)` exist. - -**Recommendation**: Adding count getters would help operators monitor pending flow health and detect stuck flows. - -### 14. Contract Version String - -**Location**: `contract.tolk:24` - -```tolk -version: "0.1.0" -``` - -**Recommendation**: Consider bumping to "1.0.0" for publish, matching EVM's `LockReleaseTokenPool 2.0.0` pattern. - ---- - -## Test Coverage Analysis - -### ✅ Covered Well - -- [x] TokenPool shared behavior tests (21 tests - access control, cursed state, chain config) -- [x] Getter verification (lockbox, token, decimals) -- [x] Pending lock creation (2 tests) -- [x] Pending release creation with JettonLockBox_Withdraw assertion -- [x] Duplicate lock rejection (exit code 48700) -- [x] Duplicate release rejection (exit code 48702) -- [x] Release flow rejection when insufficient lockbox balance -- [x] Full lock flow (partial - through AskToTransfer) -- [x] Full release flow (complete - through ReturnExcessesBack) -- [x] Cursed state blocking - -### ⚠️ Partially Covered - -- [x] Full lock flow: ends at jetton transfer to lockbox wallet, does NOT complete with JettonLockBox_Deposited callback -- [x] `onLockTransferBounced`: not tested (TODO in code) -- [x] `onReleaseWithdrawFailed`: not tested end-to-end (only tested that insufficient balance rejects upfront) - -### ❌ Not Covered - -- [ ] `onLockDepositCompleted` (JettonLockBox_Deposited callback) -- [ ] `TokenPool_LockedOrBurned` event emission -- [ ] `onLockTransferBounced` error path -- [ ] `onReleaseWithdrawFailed` callback from actual lockbox failure -- [ ] QueryId cleanup / TTL -- [ ] Forward payload validation (amount mismatch) - ---- - -## Recommendations Summary (Prioritized) - -### Fix Before Publish - -1. **Add error event + reply in `onLockTransferBounced`** (critical - silent failure) -2. **Fix error code in `onReleaseWithdrawFailed`** (misleading error code) -3. **Remove duplicate comment block** (cleanup) - -### Should Fix Before Publish - -4. **Replace comment-only test with actual test or remove it** -5. **Fix `transferInitiator` trust model** (at minimum add amount validation) -6. **Move `require('zlib')` to top-level import** - -### Nice to Have - -7. Add `getPendingLocksCount` / `getPendingReleasesCount` getters -8. Integrate real JettonLockBox in full lock flow test -9. Bump version to "1.0.0" for publish -10. Remove unused `lockboxOperator` variable -11. Consider configurable gas amounts instead of hardcoded `ton("0.1")` -12. Consider queryId TTL/cleanup mechanism