diff --git a/contracts/contracts/ccip/ccipsend_executor/contract.tolk b/contracts/contracts/ccip/ccipsend_executor/contract.tolk index 385ca1ea1..de598ff1d 100644 --- a/contracts/contracts/ccip/ccipsend_executor/contract.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/contract.tolk @@ -9,6 +9,9 @@ import "../../lib/utils" import "../router/messages" import "../fee_quoter/types" import "@stdlib/gas-payments" +import "../common/types" +import "../token_registry/messages" +import "../test/tokenPool/messages" tolk 1.4.1 @@ -46,6 +49,17 @@ fun onInternalMessage(in: InMessage) { assert(this.addresses.load().feeQuoter == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); this.onMessageValidationFailed(msg); } + // Sender must be TokenRegistry + TokenRegistry_ReturnTokenInfo => { + var this = CCIPSendExecutor.load(); + assert(this.addresses.load().tokenRegistry! == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + this.onTokenInfoReceived(msg); + } + MockTokenPool_NotifySuccessfulLockOrBurn => { + var this = CCIPSendExecutor.load(); + assert(this.state.load().tokenPool == in.senderAddress, CCIPSendExecutor_Error.Unauthorized); + this.onConfirmLockOrBurn(msg); + } else => { assert (in.body.isEmpty()) throw 0xFFFF; }, @@ -59,9 +73,16 @@ fun onBouncedMessage(in: InMessageBounced) { val this = CCIPSendExecutor.load(); this.exitWithError(CCIPSendExecutor_Error.FeeQuoterBounce); } + //TODO: We should handle bounced messages from the TokenRegistry, as that means the token is not enabled on the lane } } +//TODO: Figure out how to maintain backwards compatibility for messages sent between updating the Executor code in the OnRamp and upgrading the OnRamp itself. +// Option 1: split the initialization in two messages to maintain backwards compatibility. +// 1. CCIPSendExecutor_Execute stays the same as in 1.6.1 (that means not changing Config) +// 2. CCIPSendExecutor_InitTokenTransfer passes the token registry address and other necessary configs +// That way we can upgrade the SendExecutor code in the OnRamp and then upgrade the OnRamp itself, if an executor is deployed by the OnRamp between upgrades the new SendExecutor will still be able to handle the initialization message. +// Option 2: Create a new message type CCIPSendExecutor_ExecuteV2 which has the new config element. Make the new executor able to handle both this version and the original. fun init(onrampSend: OnRamp_Send, config: CCIPSendExecutor_Config): CCIPSendExecutor { val st = lazy CCIPSendExecutor_InitialData.fromCell(contract.getData()); return CCIPSendExecutor { @@ -70,6 +91,7 @@ fun init(onrampSend: OnRamp_Send, config: CCIPSendExecutor_Config): CCIPSendExec addresses: CCIPSendExecutor_Addresses { onramp: st.onramp, feeQuoter: config.feeQuoter, + tokenRegistry: config.tokenRegistry }.toCell(), state: CCIPSendExecutor_State_Initialized{ }.toCell(), @@ -102,13 +124,73 @@ fun getValidatedFee(feeQuoter: address, onrampSend: OnRamp_Send) { } fun CCIPSendExecutor.onMessageValidated(mutate self, msg: FeeQuoter_MessageValidated) { + val message = Router_CCIPSend.fromCell(self.onrampSend.msg); + val tokenAmounts = message.tokenAmounts; if (msg.fee.feeTokenAmount + Router_Costs.CCIPSend() > self.onrampSend.metadata.value) { self.exitWithError(CCIPSendExecutor_Error.InsufficientFunds); return; } + // If token amounts is empty finalize the message + if (tokenAmounts.empty()) { + setGasLimitToMaximum(); + self.exitSuccessfully(msg.fee); + return; + } + + // Else if there are token transfers continue querying the tokenRegistry + val tokenRegistry = self.addresses.load().tokenRegistry!; + val queryMsg = createMessage({ + bounce: true, + value: 0, + dest: tokenRegistry, + body: TokenRegistry_GetTokenInfo { + } + }); + queryMsg.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + val newState = CCIPSendExecutor { + id: self.id, + onrampSend: self.onrampSend, + addresses: self.addresses, + state: CCIPSendExecutor_State_TokenRegistryAccess { + fee: msg.fee, + }.toCell(), + }; + newState.store(); +} + +fun CCIPSendExecutor.onTokenInfoReceived(mutate self, msg: TokenRegistry_ReturnTokenInfo) { + //TODO validate sender wallet address by querying the jettonMinter + assert(msg.tokenInfo.enabled) throw CCIPSendExecutor_Error.TokenNotEnabled; + val onrampSend = lazy self.onrampSend.msg.load(); + val tokenAmount = onrampSend.tokenAmounts.iter().next(); + val addresses = lazy self.addresses.load(); + val requestLockOrBurn = createMessage({ + bounce: true, + value: 0, + dest: addresses.onramp, + body: OnRamp_ExecutorRequestsLockOrBurn { + tokenAmount, + tokenPool: msg.tokenInfo.tokenPool, + destChainSelector: onrampSend.destChainSelector, + executorID: self.id, + } + }); + requestLockOrBurn.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); + val newState = CCIPSendExecutor { + id: self.id, + onrampSend: self.onrampSend, + addresses: self.addresses, + state: CCIPSendExecutor_State_TokenTransfer { + tokenPool: msg.tokenInfo.tokenPool, + fee: self.state.load().fee, + }.toCell(), + }; + newState.store(); +} + +fun CCIPSendExecutor.onConfirmLockOrBurn(mutate self, msg: MockTokenPool_NotifySuccessfulLockOrBurn) { setGasLimitToMaximum(); - - self.exitSuccessfully(msg.fee); + self.exitSuccessfully(self.state.load().fee); } fun CCIPSendExecutor.exitSuccessfully(self, fee: Fee) { diff --git a/contracts/contracts/ccip/ccipsend_executor/errors.tolk b/contracts/contracts/ccip/ccipsend_executor/errors.tolk index d6a7b9fc1..bf0e3dd02 100644 --- a/contracts/contracts/ccip/ccipsend_executor/errors.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/errors.tolk @@ -7,4 +7,5 @@ enum CCIPSendExecutor_Error { InsufficientFunds InsufficientFee FeeQuoterBounce + TokenNotEnabled } diff --git a/contracts/contracts/ccip/ccipsend_executor/messages.tolk b/contracts/contracts/ccip/ccipsend_executor/messages.tolk index ac354427d..8fb43ba36 100644 --- a/contracts/contracts/ccip/ccipsend_executor/messages.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/messages.tolk @@ -3,11 +3,16 @@ import "types" import "../onramp/messages" import "../fee_quoter/messages" +import "../token_registry/messages" +import "../common/types" +import "../test/tokenPool/messages" type CCIPSendExecutor_InMessage = | CCIPSendExecutor_Execute | FeeQuoter_MessageValidated | FeeQuoter_MessageValidationFailed + | TokenRegistry_ReturnTokenInfo + | MockTokenPool_NotifySuccessfulLockOrBurn ; // crc32('CCIPSendExecutor_Execute') @@ -55,4 +60,4 @@ fun SendExecutor_Costs.MessageValidationFailed(): int { type CCIPSendExecutor_BouncedMessage = | FeeQuoter_GetValidatedFee - ; \ No newline at end of file + ; diff --git a/contracts/contracts/ccip/ccipsend_executor/types.tolk b/contracts/contracts/ccip/ccipsend_executor/types.tolk index d1fdde01a..9905145cb 100644 --- a/contracts/contracts/ccip/ccipsend_executor/types.tolk +++ b/contracts/contracts/ccip/ccipsend_executor/types.tolk @@ -1,5 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 import "../onramp/messages.tolk"; +import "../fee_quoter/types" const CCIPSendExecutor_FACILITY_NAME = "link.chain.ton.ccip.CCIPSendExecutor"; const CCIPSendExecutor_FACILITY_ID = CCIPSendExecutor_FACILITY_NAME.crc32() % 640 + 10; // 178 @@ -22,11 +23,14 @@ struct CCIPSendExecutor_Data { struct CCIPSendExecutor_Addresses { onramp: address, feeQuoter: address, + tokenRegistry: address?, } type CCIPSendExecutor_State = | Cell | Cell + | Cell + | Cell | Cell struct CCIPSendExecutor_State_Initialized { @@ -35,11 +39,25 @@ struct CCIPSendExecutor_State_Initialized { struct CCIPSendExecutor_State_OnGoingFeeValidation { } +struct CCIPSendExecutor_State_TokenRegistryAccess { + fee: Fee, +} + +//TODO Impl wallet address validation through JettonMinter/ stored wallet code +struct CCIPSendExecutor_State_WalletAddressValidation { +} + +struct CCIPSendExecutor_State_TokenTransfer { + tokenPool: address, + fee: Fee, +} + struct CCIPSendExecutor_State_Finalized { } struct CCIPSendExecutor_Config { feeQuoter: address, + tokenRegistry: address?, } diff --git a/contracts/contracts/ccip/fee_quoter/contract.tolk b/contracts/contracts/ccip/fee_quoter/contract.tolk index 42afd8095..724d12b86 100644 --- a/contracts/contracts/ccip/fee_quoter/contract.tolk +++ b/contracts/contracts/ccip/fee_quoter/contract.tolk @@ -277,9 +277,10 @@ fun calculateValidatedFee(msg: Router_CCIPSend): Fee { val (executionGasPrice: uint112, dataAvailabilityGasPrice: uint112) = destChainConfig.getValidatedGasPrice(); - val tokensIter = msg.tokenAmounts.iter(); val premiumFeeUsdWei = mustProd(destChainConfig.config.networkFeeUsdCents, VAL_1E16, FeeQuoter_Error.PremiumFeeOverflow); - assert (tokensIter.empty()) throw FeeQuoter_Error.UnsupportedNumberOfTokens; + // NOTE: token transfers are currently priced as if the message carried no tokens + // (the extra token-transfer fee is ignored). The blocking empty-tokens assert was removed + // to enable the token-transfer send flow. val dataAvailabilityCost = _dataAvailabilityCost( destChainConfig, @@ -366,8 +367,7 @@ fun _dataAvailabilityCost( fun validateMessageAndResolveGasLimitForDestination(extraArgs: cell, config: FeeQuoterDestChainConfig, message: Router_CCIPSend, msgDataLen: uint256): int { // Check that payload is formed correctly. assert (msgDataLen <= config.maxDataBytes) throw FeeQuoter_Error.MsgDataTooLarge; - val tokenAmounts = message.tokenAmounts.iter(); - assert (tokenAmounts.empty()) throw FeeQuoter_Error.UnsupportedNumberOfTokens; + // NOTE: token transfers are allowed; their extra fee is currently ignored (priced as a plain message). // NOTE: we could deploy distinct contracts to cut down on code if (config.chainFamilySelector == CHAIN_FAMILY_SELECTOR_EVM || diff --git a/contracts/contracts/ccip/onramp/contract.tolk b/contracts/contracts/ccip/onramp/contract.tolk index 925756e27..b74820cfd 100644 --- a/contracts/contracts/ccip/onramp/contract.tolk +++ b/contracts/contracts/ccip/onramp/contract.tolk @@ -72,6 +72,12 @@ fun onInternalMessage(in: InMessage) { assert(destChainConfig.router == in.senderAddress, Error.Unauthorized); onSend(st, destChainConfig, msg) } + OnRamp_ExecutorRequestsLockOrBurn => { + // Sender must be Send Executor + var st = lazy OnRamp_Storage.load(); + assert(st.executor.autoDeployAddress(msg.executorID).calculateAddress() == in.senderAddress, Error.Unauthorized); + onExecutorRequestsLockOrBurn(st, msg, in.senderAddress); + } // Sender must be Send Executor OnRamp_ExecutorFinishedSuccessfully => { val st = lazy OnRamp_Storage.load(); @@ -252,6 +258,14 @@ fun onSend(st: OnRamp_Storage, destChainConfig: OnRamp_DestChainConfig, payload: val config = st.config.load(); + var tokenRegistry: address? = null; + + val msg = payload.msg.load(); + var tokenAmounts = msg.tokenAmounts.iter(); + if (!tokenAmounts.empty()) { + tokenRegistry = calculateTokenRegistryAddress(st, tokenAmounts.next().token); + } + val executorId = generateRandomSendExecutorId(); val executeMsg = createMessage({ bounce: true, @@ -271,6 +285,7 @@ fun onSend(st: OnRamp_Storage, destChainConfig: OnRamp_DestChainConfig, payload: onrampSend: payload, config: CCIPSendExecutor_Config { feeQuoter: config.feeQuoter, + tokenRegistry, }.toCell(), }.toCell(), }, @@ -282,6 +297,28 @@ fun onSend(st: OnRamp_Storage, destChainConfig: OnRamp_DestChainConfig, payload: st.store(); } +fun calculateTokenRegistryAddress(st: OnRamp_Storage, token: address): address { + // TODO: resolve a per-token registry address. For now the OnRamp stores a single + // TokenRegistry address that is used for every token transfer. + return st.tokenRegistry! +} + +fun onExecutorRequestsLockOrBurn(st: OnRamp_Storage, msg: OnRamp_ExecutorRequestsLockOrBurn, sender: address) { + var destChainConfig = st.destChainConfigs.mustGet(msg.destChainSelector, Error.UnknownDestChainSelector as int); + val routerLockOrBurn = createMessage({ + bounce: true, + value: 0, + dest: destChainConfig.router, + body: Router_LockOrBurn { + tokenPool: msg.tokenPool, + tokenAmount: msg.tokenAmount, + destChainSelector: msg.destChainSelector, + executorAddress: sender, + } + }); + routerLockOrBurn.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + /* Handles successful completion of a CCIPSendExecutor. Assigns a new sequence number and notifies the Router of the successful send. diff --git a/contracts/contracts/ccip/onramp/messages.tolk b/contracts/contracts/ccip/onramp/messages.tolk index cb386370c..a24959b34 100644 --- a/contracts/contracts/ccip/onramp/messages.tolk +++ b/contracts/contracts/ccip/onramp/messages.tolk @@ -7,12 +7,14 @@ import "../ccipsend_executor/messages" import "../fee_quoter/messages" import "../ccipsend_executor/types" import "../fee_quoter/types" +import "../common/types" type OnRamp_InMessage = | OnRamp_Send | OnRamp_GetValidatedFee | FeeQuoter_MessageValidated | FeeQuoter_MessageValidationFailed + | OnRamp_ExecutorRequestsLockOrBurn | OnRamp_ExecutorFinishedSuccessfully | OnRamp_ExecutorFinishedWithError | OnRamp_SetDynamicConfig @@ -38,6 +40,13 @@ struct (0x9c2ccc7e) OnRamp_GetValidatedFee { context: T; } +struct (0x9be1fb61) OnRamp_ExecutorRequestsLockOrBurn { + tokenAmount: TokenAmount, + tokenPool: address, + destChainSelector: uint64, + executorID: CCIPSendExecutor_ID, +} + struct OnRamp_GetValidatedFeeContext { onrampContext: address; // router address userContext: RemainingBitsOrRef, @@ -168,4 +177,4 @@ fun OnRamp_Costs.WithdrawFeeTokens(): int { + ton("0.019") // feeTokens fwdFee + ton("0.05") // feeCollector computeFee ; -} \ No newline at end of file +} diff --git a/contracts/contracts/ccip/onramp/storage.tolk b/contracts/contracts/ccip/onramp/storage.tolk index 505563a88..3e50402e5 100644 --- a/contracts/contracts/ccip/onramp/storage.tolk +++ b/contracts/contracts/ccip/onramp/storage.tolk @@ -14,6 +14,10 @@ struct OnRamp_Storage { destChainConfigs: map; // chainSelector -> DestChainConfig executor: ExecutorDeployment; + + // Address of the TokenRegistry queried by the CCIPSendExecutor during token transfers. + // Null until token transfers are configured. TODO: replace with per-token registry resolution. + tokenRegistry: address?; } fun OnRamp_Storage.load(): OnRamp_Storage { diff --git a/contracts/contracts/ccip/onramp/types.tolk b/contracts/contracts/ccip/onramp/types.tolk index ab205e7c6..0b7ddd197 100644 --- a/contracts/contracts/ccip/onramp/types.tolk +++ b/contracts/contracts/ccip/onramp/types.tolk @@ -70,7 +70,7 @@ struct TVM2AnyRampMessageBody { receiver: Cell; data: cell; extraArgs: cell; - tokenAmounts: cell; + tokenAmounts: SnakedCell; feeToken: address; feeTokenAmount: uint256; } diff --git a/contracts/contracts/ccip/router/contract.tolk b/contracts/contracts/ccip/router/contract.tolk index 77df8cd15..b377370ec 100644 --- a/contracts/contracts/ccip/router/contract.tolk +++ b/contracts/contracts/ccip/router/contract.tolk @@ -13,6 +13,8 @@ import "../onramp/messages" import "../onramp/types" import "../offramp/messages" import "../offramp/types" +import "../common/messages" +import "../test/tokenPool/messages" import "types" import "messages" @@ -53,6 +55,8 @@ fun onInternalMessage(in: InMessage) { Router_CCIPSend => { val minValue = Router_Costs.CCIPSend(); assert (in.valueCoins >= minValue) throw Router_Error.InsufficientFee; + // Token transfer messages can only come in through jetton transfer notifications + assert(msg.tokenAmounts.empty()) throw Router_Error.TokenTransferNotThroughNotification; //TBD shuold this be sendMessageRejected instead of throw? onCCIPSend(msg, in.senderAddress, in.valueCoins); } // Permissionless @@ -155,6 +159,33 @@ fun onInternalMessage(in: InMessage) { onSendNACK(msg) } + Router_LockOrBurn => { + val st = lazy Storage.load(); + assertSenderIsOnRamp(st, msg.destChainSelector, in.senderAddress); + onLockOrBurn(msg); + } + // Sender must be a Jetton Wallet owned by the Router + Common_JettonTransferNotification => { + + // TODO minValue is probably higher for a tokenTransfer as it needs more hops. + // We should revisit our entire min gas assertion model anyway + val minValue = Router_Costs.CCIPSend(); + assert (in.valueCoins >= minValue) throw Router_Error.InsufficientFee; + + assert (msg.forwardPayload != null) throw Router_Error.JettonTransferNotificationWithoutForwardPayload; + + //TODO: should we catch cases when the forward payload is not Router_CCIPSend? + val sendMsg = lazy Router_CCIPSend.fromCell(msg.forwardPayload); + + var tokenAmounts = sendMsg.tokenAmounts.iter(); + assert (!tokenAmounts.empty()) throw Router_Error.MissingTokenAmounts; + + val transferedJetton = tokenAmounts.next(); + assert (transferedJetton.amount == msg.amount) throw Router_Error.TokenAmountMismatch; + assert (tokenAmounts.empty()) throw Router_Error.NoMultiTokenTransfers; + + onCCIPSend(sendMsg, msg.sender, in.valueCoins); + } else => { // attempt to handle Ownable messages var st = lazy Storage.load(); @@ -619,6 +650,20 @@ fun sendMessageRejected(sender: address, queryID: uint64, error: uint256) { nackMsg.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); } +//TODO Should be a jetton transfer to the TokenPool wallet, and the LockOrBurn message should go in the forwardPayload of that jetton transfer +fun onLockOrBurn(msg: Router_LockOrBurn) { + val tokenPoolLockOrBurn = createMessage({ + bounce: true, + value: 0, + dest: msg.tokenPool, + body: MockTokenPool_LockOrBurn { + tokenAmount: msg.tokenAmount, + notify: msg.executorAddress, + } + }); + tokenPoolLockOrBurn.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} + /* Handles contract upgrade requests using the Upgradeable pattern. Invokes the upgrade process with the current migrate function and version metadata. diff --git a/contracts/contracts/ccip/router/errors.tolk b/contracts/contracts/ccip/router/errors.tolk index a8e1d2192..1cfdee07f 100644 --- a/contracts/contracts/ccip/router/errors.tolk +++ b/contracts/contracts/ccip/router/errors.tolk @@ -12,4 +12,8 @@ enum Router_Error { MissingTokenAmounts NoMultiTokenTransfers InsufficientFee + TokenTransferNotThroughNotification + JettonTransferNotificationWithoutForwardPayload + TokenAmountMismatch + ForwardPayloadIsNotCCIPSend } diff --git a/contracts/contracts/ccip/router/messages.tolk b/contracts/contracts/ccip/router/messages.tolk index 2b8dc9164..bd163e876 100644 --- a/contracts/contracts/ccip/router/messages.tolk +++ b/contracts/contracts/ccip/router/messages.tolk @@ -9,6 +9,7 @@ import "../../lib/receiver/messages" import "../offramp/types" import "../offramp/messages" import "../onramp/messages" +import "../common/messages" type Router_InMsg = | Router_CCIPSend @@ -25,7 +26,9 @@ type Router_InMsg = | Router_RMNRemoteVerifyNotCursed | Router_MessageSent | Router_MessageRejected + | Router_LockOrBurn | Router_RMNOwnableMessage + | Common_JettonTransferNotification ; type Router_GetValidatedFee_RemainingBitsAndRefs = Router_GetValidatedFee @@ -123,6 +126,13 @@ struct (0x4dd6aa82) Router_GetValidatedFee { context: T; } +struct (0x6f2d00df) Router_LockOrBurn { + tokenPool: address, + tokenAmount: TokenAmount, + destChainSelector: uint64, + executorAddress: address, +} + struct Router_GetValidatedFeeContext { routerContext: address; // sender userContext: RemainingBitsOrRef, diff --git a/contracts/contracts/ccip/test/tokenPool/contract.tolk b/contracts/contracts/ccip/test/tokenPool/contract.tolk new file mode 100644 index 000000000..13c48caf8 --- /dev/null +++ b/contracts/contracts/ccip/test/tokenPool/contract.tolk @@ -0,0 +1,19 @@ +import "messages" + +contract MockTokenPool { + author: "SmartContract Chainlink Limited SEZC" + version: "1.6.1" + description: "link.chain.ton.ccip.test.TokenPool" + incomingMessages: MockTokenPool_LockOrBurn +} + +fun onInternalMessage(in: InMessage) { + val msg = lazy MockTokenPool_LockOrBurn.fromSlice(in.body); + val notification = createMessage({ + bounce: false, + value: 0, + dest: msg.notify, + body: MockTokenPool_NotifySuccessfulLockOrBurn {}, + }); + notification.send(SEND_MODE_CARRY_ALL_BALANCE); +} diff --git a/contracts/contracts/ccip/test/tokenPool/messages.tolk b/contracts/contracts/ccip/test/tokenPool/messages.tolk new file mode 100644 index 000000000..1476613ae --- /dev/null +++ b/contracts/contracts/ccip/test/tokenPool/messages.tolk @@ -0,0 +1,10 @@ +import "../../common/types" + +struct (0x7dd8f942) MockTokenPool_LockOrBurn { + tokenAmount: TokenAmount, + notify: address, +} + +struct (0x7adb20bb) MockTokenPool_NotifySuccessfulLockOrBurn {} + + diff --git a/contracts/contracts/ccip/token_registry/contract.tolk b/contracts/contracts/ccip/token_registry/contract.tolk new file mode 100644 index 000000000..d80879b29 --- /dev/null +++ b/contracts/contracts/ccip/token_registry/contract.tolk @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +import "messages" +import "storage" + +tolk 1.4.1 + +contract TokenRegistry { + author: "SmartContract Chainlink Limited SEZC" + version: "1.6.1" + description: "link.chain.ton.ccip.TokenRegistry" + + storage: TokenRegistry_Storage + incomingMessages: TokenRegistry_InMessage +} + +fun onInternalMessage(in: InMessage) { + val msg = lazy TokenRegistry_InMessage.fromSlice(in.body); + + match (msg) { + TokenRegistry_GetTokenInfo => { + //TODO: min value assertion + onGetTokenInfo(msg, in.senderAddress); + } + else => { + assert (in.body.isEmpty()) throw 0xFFFF; + } + } +} + +fun onGetTokenInfo(msg: TokenRegistry_GetTokenInfo, sender: address) { + val st = TokenRegistry_Storage.load(); + //TODO we might want to send a different message than ReturnTokenInfo when the token is not enabled + val response = createMessage({ + dest: sender, + value: 0, + bounce: false, + body: TokenRegistry_ReturnTokenInfo { + tokenInfo: st.info, + } + }); + response.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE); +} diff --git a/contracts/contracts/ccip/token_registry/messages.tolk b/contracts/contracts/ccip/token_registry/messages.tolk new file mode 100644 index 000000000..ead3d1667 --- /dev/null +++ b/contracts/contracts/ccip/token_registry/messages.tolk @@ -0,0 +1,13 @@ +import "types" + +type TokenRegistry_InMessage = + | TokenRegistry_GetTokenInfo; + +// crc32('TokenRegistry_GetTokenInfo') +struct (0xDD5D5127) TokenRegistry_GetTokenInfo { +} + +// Outgoing messages +struct (0xddccddb5) TokenRegistry_ReturnTokenInfo { + tokenInfo: TokenRegistry_TokenInfo, +} diff --git a/contracts/contracts/ccip/token_registry/storage.tolk b/contracts/contracts/ccip/token_registry/storage.tolk new file mode 100644 index 000000000..5c1064339 --- /dev/null +++ b/contracts/contracts/ccip/token_registry/storage.tolk @@ -0,0 +1,15 @@ +import "types" + +struct TokenRegistry_Storage { + info: TokenRegistry_TokenInfo +} + +fun TokenRegistry_Storage.load(): TokenRegistry_Storage { + return TokenRegistry_Storage.fromCell(contract.getData()); +} + +fun TokenRegistry_Storage.store(self) { + contract.setData(TokenRegistry_Storage{ + info: self.info, + }.toCell()); +} diff --git a/contracts/contracts/ccip/token_registry/types.tolk b/contracts/contracts/ccip/token_registry/types.tolk new file mode 100644 index 000000000..8ebddf3ca --- /dev/null +++ b/contracts/contracts/ccip/token_registry/types.tolk @@ -0,0 +1,5 @@ +struct TokenRegistry_TokenInfo { + tokenPool: address + minterAddress: address + enabled: bool +} diff --git a/contracts/tests/Logs.ts b/contracts/tests/Logs.ts index eb17f269b..46b56f508 100644 --- a/contracts/tests/Logs.ts +++ b/contracts/tests/Logs.ts @@ -7,6 +7,7 @@ import * as offRamp from '../wrappers/ccip/OffRamp' import { prettifyAddressesMap } from './utils/prettyPrint' import { crc32 } from 'zlib' import * as onramp from '../wrappers/ccip/OnRamp' +import * as router from '../wrappers/ccip/Router' // https://github.com/ton-blockchain/liquid-staking-contract/blob/1f4e9badbed52a4cf80cc58e4bb36ed375c6c8e7/utils.ts#L269-L294 export const getExternals = (transactions: BlockchainTransaction[]) => { @@ -226,11 +227,23 @@ export const testLogCCIPMessageSent = ( const msg: onramp.CCIPMessageSent = onramp.builder.events.ccipMessageSent.load(x.beginParse()) const sender = msg.message.sender + // Decode tokenAmounts from its raw Cell into an array so matches can use plain objects. + const decodedMessage = { + ...msg.message, + body: { + ...msg.message.body, + tokenAmounts: fromSnakeData( + msg.message.body.tokenAmounts, + router.builder.data.tokenAmount.load, + ), + }, + } + // Check other fields using toMatchObject (excluding sender to avoid object comparison) - const { sender: _, ...messageWithoutSender } = msg.message + const { sender: _, ...messageWithoutSender } = decodedMessage const { sender: __, ...matchWithoutSender } = match.message || {} - matchesObject(messageWithoutSender, matchWithoutSender) + matchesObject(messageWithoutSender, matchWithoutSender as object) // Check sender address using .equals() if specified in match if (match.message?.sender && match.message.sender instanceof Address) { diff --git a/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts b/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts new file mode 100644 index 000000000..0d4e53c54 --- /dev/null +++ b/contracts/tests/ccip/e2e/CCIPSendWithTokenTransfer.spec.ts @@ -0,0 +1,296 @@ +import '@ton/test-utils' +import { compile } from '@ton/blueprint' +import { toNano, Cell, Address, beginCell } from '@ton/core' +import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox' + +import { LogTypes } from '../../../wrappers/ccip/Logs' +import { assertLog } from '../../Logs' +import { WRAPPED_NATIVE } from '../../../src/utils' + +import * as fq from '../../../wrappers/ccip/FeeQuoter' +import * as or from '../../../wrappers/ccip/OnRamp' +import * as rt from '../../../wrappers/ccip/Router' +import { TokenRegistry } from '../../../wrappers/ccip/TokenRegistry' +import { MockTokenPool } from '../../../wrappers/ccip/MockTokenPool' +import { JettonMinter } from '../../../wrappers/jetton/JettonMinter' +import { JettonWallet } from '../../../wrappers/jetton/JettonWallet' +import { WTON_MINT_OPCODE } from '../../../wrappers/wton' + +import { setup, CHAINSEL_EVM_TEST_90000001, EVM_ADDRESS } from '../router/Router.Setup' + +const JETTON_CONTENT = beginCell().storeStringTail('wton.e2e').endCell() + +// Amount of wTON the user transfers (also the CCIP tokenAmount). Deliberately different from +// FORWARD_TON_AMOUNT so the test can prove metadata.value is the attached native TON, not the +// transferred token amount (fees are paid in native TON, not in the transferred token). +const TOKEN_AMOUNT = toNano('5') + +// Native TON attached to the transfer notification, used to pay fees + execution costs. +const FORWARD_TON_AMOUNT = toNano('1') + +describe('CCIPSend with token transfer (e2e)', () => { + let blockchain: Blockchain + + let minterCode: Cell + let walletCode: Cell + + let deployer: SandboxContract + let sender: SandboxContract + + let minter: SandboxContract + let mockTokenPool: SandboxContract + let tokenRegistry: SandboxContract + + let router: SandboxContract + let feeQuoter: SandboxContract + let onRamp: SandboxContract + + beforeAll(async () => { + minterCode = await compile('wton.JettonMinter') + walletCode = await compile('wton.JettonWallet') + }) + + beforeEach(async () => { + blockchain = await Blockchain.create() + blockchain.verbosity = { + print: true, + blockchainLogs: false, + vmLogs: 'none', + debugLogs: true, + } + + deployer = await blockchain.treasury('deployer') + sender = await blockchain.treasury('sender') + + // 1. Deploy the wTON jetton minter. + minter = blockchain.openContract( + JettonMinter.createFromConfig( + { + admin: null, + transferAdmin: null, + walletCode, + jettonContent: JETTON_CONTENT, + totalSupply: 0n, + }, + minterCode, + ), + ) + await minter.sendTopUpTons(deployer.getSender(), toNano('0.01')) + + // 2. Mint wTON to the user (deploys the user's wallet with a balance). + await minter.sendMint(deployer.getSender(), { + value: TOKEN_AMOUNT + toNano('1') + toNano('0.3'), + mintOpcode: WTON_MINT_OPCODE, + message: { + queryId: 0n, + destination: sender.address, + tonAmount: toNano('1'), + jettonAmount: TOKEN_AMOUNT, + from: null, + responseDestination: sender.address, + forwardTonAmount: 0n, + customPayload: null, + }, + }) + + // 3. Deploy the MockTokenPool that performs the (mock) lock/burn. + const mockTokenPoolCode = await MockTokenPool.code() + mockTokenPool = blockchain.openContract(MockTokenPool.createFromConfig(mockTokenPoolCode)) + await mockTokenPool.sendDeploy(deployer.getSender(), toNano('0.05')) + + // 4. Deploy the TokenRegistry, hard-coded to return the MockTokenPool address. + const tokenRegistryCode = await TokenRegistry.code() + tokenRegistry = blockchain.openContract( + TokenRegistry.createFromConfig( + { + info: { + tokenPool: mockTokenPool.address, + minterAddress: minter.address, + enabled: true, + }, + }, + tokenRegistryCode, + ), + ) + await tokenRegistry.sendDeploy(deployer.getSender(), toNano('0.05')) + + // 5. Deploy router/feeQuoter/onRamp/offRamp, storing the TokenRegistry in the OnRamp. + ;({ router, feeQuoter, onRamp } = await setup(blockchain, { + deployer, + sender, + tokenRegistry: tokenRegistry.address, + })) + }) + + it('propagates a token-transfer-initiated CCIP send end to end', async () => { + const ccipSend: rt.CCIPSend = { + queryID: 1, + destChainSelector: CHAINSEL_EVM_TEST_90000001, + receiver: EVM_ADDRESS, + data: Cell.EMPTY, + tokenAmounts: [{ amount: TOKEN_AMOUNT, token: minter.address }], + feeToken: WRAPPED_NATIVE, + extraArgs: rt.builder.data.extraArgs + .encode({ + kind: 'generic-v2', + gasLimit: 100n, + allowOutOfOrderExecution: true, + }) + .asCell(), + } + + // The CCIPSend payload travels as the forward payload of the jetton transfer. + const forwardPayload = rt.builder.message.in.ccipSend.encode(ccipSend).endCell() + + const routerWalletAddress = await minter.getWalletAddress(router.address) + const senderWallet = blockchain.openContract( + JettonWallet.createFromAddress(await minter.getWalletAddress(sender.address)), + ) + + // User transfers wTON to the router-owned wallet, carrying the CCIPSend payload. + const result = await senderWallet.sendTransfer(sender.getSender(), { + value: FORWARD_TON_AMOUNT + toNano('2'), + message: { + queryId: 1, + jettonAmount: TOKEN_AMOUNT, + destination: router.address, + responseDestination: sender.address, + customPayload: null, + forwardTonAmount: FORWARD_TON_AMOUNT, + forwardPayload, + }, + }) + + // Discover the deployed CCIPSendExecutor (first message emitted by the OnRamp). + const executorAddress = ((): Address => { + for (const tx of result.transactions) { + const inMsg = tx.inMessage + if ( + inMsg?.info.type === 'internal' && + inMsg.info.src instanceof Address && + inMsg.info.src.equals(onRamp.address) && + inMsg.info.dest instanceof Address + ) { + return inMsg.info.dest + } + } + throw new Error('Executor address not found') + })() + + // --- jetton transfer leg --- + // user -> user wallet + expect(result.transactions).toHaveTransaction({ + from: sender.address, + to: senderWallet.address, + success: true, + }) + // user wallet -> router wallet (deploys it) + expect(result.transactions).toHaveTransaction({ + from: senderWallet.address, + to: routerWalletAddress, + deploy: true, + success: true, + }) + // router wallet -> router (transfer notification) + expect(result.transactions).toHaveTransaction({ + from: routerWalletAddress, + to: router.address, + success: true, + }) + + // --- ccip send leg --- + // router -> onRamp + expect(result.transactions).toHaveTransaction({ + from: router.address, + to: onRamp.address, + success: true, + }) + // onRamp deploys the executor + expect(result.transactions).toHaveTransaction({ + from: onRamp.address, + to: executorAddress, + deploy: true, + success: true, + }) + // executor -> feeQuoter and back + expect(result.transactions).toHaveTransaction({ + from: executorAddress, + to: feeQuoter.address, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: feeQuoter.address, + to: executorAddress, + success: true, + }) + // executor -> tokenRegistry and back + expect(result.transactions).toHaveTransaction({ + from: executorAddress, + to: tokenRegistry.address, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: tokenRegistry.address, + to: executorAddress, + success: true, + }) + // executor -> onRamp (requests lock/burn) + expect(result.transactions).toHaveTransaction({ + from: executorAddress, + to: onRamp.address, + success: true, + }) + // onRamp -> router (forwards lock/burn) + expect(result.transactions).toHaveTransaction({ + from: onRamp.address, + to: router.address, + success: true, + }) + // router -> mockTokenPool (lock/burn) and back to the executor (confirmation) + expect(result.transactions).toHaveTransaction({ + from: router.address, + to: mockTokenPool.address, + success: true, + }) + expect(result.transactions).toHaveTransaction({ + from: mockTokenPool.address, + to: executorAddress, + success: true, + }) + // executor -> onRamp (finished successfully) and self-destructs + expect(result.transactions).toHaveTransaction({ + from: executorAddress, + to: onRamp.address, + success: true, + }) + + // OnRamp emits the CCIPMessageSent log. Verify the token-transfer amount equals TOKEN_AMOUNT (wTON). + assertLog(result.transactions, onRamp.address, LogTypes.CCIPMessageSent, { + message: { + header: { + destChainSelector: CHAINSEL_EVM_TEST_90000001, + }, + sender: sender.address, + body: { + tokenAmounts: [{ amount: TOKEN_AMOUNT, token: minter.address }], + }, + }, + } as any) + + // OnRamp -> router (Router_MessageSent) + expect(result.transactions).toHaveTransaction({ + from: onRamp.address, + to: router.address, + op: rt.opcodes.in.messageSent, + success: true, + }) + + // router -> user (CCIPSendACK) + expect(result.transactions).toHaveTransaction({ + from: router.address, + to: sender.address, + op: rt.opcodes.out.ccipSendACK, + success: true, + }) + }) +}) diff --git a/contracts/tests/ccip/feequoter/FeeQuoter.getValidatedFee.spec.ts b/contracts/tests/ccip/feequoter/FeeQuoter.getValidatedFee.spec.ts index d29c99852..74c49150a 100644 --- a/contracts/tests/ccip/feequoter/FeeQuoter.getValidatedFee.spec.ts +++ b/contracts/tests/ccip/feequoter/FeeQuoter.getValidatedFee.spec.ts @@ -313,57 +313,23 @@ describe('FeeQuoter GetValidatedFee', () => { }) }) - it('should revert when too many tokens', async () => { - const tooManyTokens = [FeeQuoterSetup.SOURCE_FEE_TOKEN] // We don't support token transfers in TON yet - - const message: rt.CCIPSend = { - destChainSelector: FeeQuoterSetup.DEST_CHAIN_SELECTOR_EVM, - receiver: FeeQuoterSetup.DEST_ADDRESS, - data: beginCell().endCell(), - tokenAmounts: tooManyTokens.map((token) => ({ - token: token.token, - amount: toNano('100'), - })), - feeToken: FeeQuoterSetup.NATIVE_TON.token, - extraArgs: rt.builder.data.extraArgs - .encode({ - kind: 'generic-v2', - gasLimit: BigInt(FeeQuoterSetup.GAS_LIMIT), - allowOutOfOrderExecution: false, - }) - .endCell(), - } + it('accepts a token transfer and prices it like a token-less message', async () => { + // Token transfers are now allowed; the extra token-transfer fee is currently ignored, + // so a single-token message is priced exactly like the equivalent token-less message. + const feeToken = FeeQuoterSetup.NATIVE_TON.token + + const withToken = setup.generateSingleTokenMessage({ + token: FeeQuoterSetup.SOURCE_FEE_TOKEN.token, + amount: toNano('100'), + feeToken, + }) + const withoutToken = setup.generateEmptyMessage({ feeToken }) - const result = await setup.bind.feeQuoter.sendGetValidatedFee( - setup.acc.externalCaller.getSender(), - { - value: toNano('1'), - msg: { msg: message, context: beginCell().asSlice() }, - }, - ) + // getValidatedFee throws if validation fails, so reaching the assertion proves acceptance. + const tokenFee = await setup.getValidatedFee(withToken) + const emptyFee = await setup.getValidatedFee(withoutToken) - // Should return failure - destination chain not configured - expect(result.transactions).toHaveTransaction({ - from: setup.acc.externalCaller.getSender().address, - to: setup.bind.feeQuoter.address, - success: true, - }) - expect(result.transactions).toHaveTransaction({ - from: setup.bind.feeQuoter.address, - op: sx.opcodes.in.messageValidationFailed, - success: true, - body(x) { - return verifyBodyMessage( - x, - sx.builder.message.in.messageValidationFailed, - [ - (msg) => { - return msg.error === BigInt(feeQuoter.errors.UnsupportedNumberOfTokens) - }, - ], - ) - }, - }) + expect(tokenFee.fee.feeTokenAmount).toEqual(emptyFee.fee.feeTokenAmount) }) it('should revert when gas limit too high', async () => { diff --git a/contracts/tests/ccip/router/Router.Setup.ts b/contracts/tests/ccip/router/Router.Setup.ts index 0f0a47f41..fb0af4fc1 100644 --- a/contracts/tests/ccip/router/Router.Setup.ts +++ b/contracts/tests/ccip/router/Router.Setup.ts @@ -20,6 +20,8 @@ type RouterSetupOptionsCommon = { receiver?: SandboxContract router?: SandboxContract skipRouterOnRampConfig?: boolean + // Optional TokenRegistry address stored in the OnRamp, used for token-transfer sends. + tokenRegistry?: Address } type RouterSetupOverrides = Partial<{ feeQuoter: SandboxContract | SandboxContract @@ -80,7 +82,13 @@ export async function setup( const feeQuoter = opts.feeQuoter ?? (await deployFeeQuoterInstance(blockchain, deployer)) const onRamp = opts.onRamp ?? - (await deployOnRampInstance(blockchain, deployer, router.address, feeQuoter.address)) + (await deployOnRampInstance( + blockchain, + deployer, + router.address, + feeQuoter.address, + opts.tokenRegistry, + )) const offRamp = opts.offRamp ?? @@ -262,6 +270,7 @@ async function deployOnRampInstance( deployer: SandboxContract, router: Address, feeQuoter: Address, + tokenRegistry?: Address, ) { const code = await contractCode.ccip.local('OnRamp') const data: or.OnRampStorage = { @@ -282,6 +291,7 @@ async function deployOnRampInstance( deployableCode: await contractCode.ccip.local('Deployable'), executorCode: await contractCode.ccip.local('CCIPSendExecutor'), }, + tokenRegistry: tokenRegistry ?? null, } const onRamp = blockchain.openContract(or.OnRamp.createFromConfig(data, code)) diff --git a/contracts/wrappers/ccip.TokenRegistry.compile.ts b/contracts/wrappers/ccip.TokenRegistry.compile.ts new file mode 100644 index 000000000..4a752e2f9 --- /dev/null +++ b/contracts/wrappers/ccip.TokenRegistry.compile.ts @@ -0,0 +1,7 @@ +import { CompilerConfig } from '@ton/blueprint' + +export const compile: CompilerConfig = { + lang: 'tolk', + entrypoint: 'contracts/ccip/token_registry/contract.tolk', + withStackComments: true, +} diff --git a/contracts/wrappers/ccip.test.mockTokenPool.compile.ts b/contracts/wrappers/ccip.test.mockTokenPool.compile.ts new file mode 100644 index 000000000..872f6058a --- /dev/null +++ b/contracts/wrappers/ccip.test.mockTokenPool.compile.ts @@ -0,0 +1,10 @@ +import { CompilerConfig } from '@ton/blueprint' + +// NOTE: Receiver contract moved from examples/ to ccip/test/ so it gets included +// in published release artifacts (examples.* contracts are excluded from releases). +// See: .github/workflows/contracts-publish-compiled-artifacts.yml +export const compile: CompilerConfig = { + lang: 'tolk', + entrypoint: 'contracts/ccip/test/tokenPool/contract.tolk', + withStackComments: true, +} diff --git a/contracts/wrappers/ccip/CCIPSendExecutor.ts b/contracts/wrappers/ccip/CCIPSendExecutor.ts index ed95d315d..a68d4bb6b 100644 --- a/contracts/wrappers/ccip/CCIPSendExecutor.ts +++ b/contracts/wrappers/ccip/CCIPSendExecutor.ts @@ -48,9 +48,16 @@ export type Data = { export type Addresses = { onramp: Address feeQuoter: Address + // Null when the send carries no token transfers. + tokenRegistry?: Address | null } -export type State = Initialized | OnGoingFeeValidation +export type State = + | Initialized + | OnGoingFeeValidation + | TokenRegistryAccess + | TokenTransfer + | Finalized export type Initialized = { kind: 'initialized' @@ -60,8 +67,25 @@ export type OnGoingFeeValidation = { kind: 'on-going-fee-validation' } +export type TokenRegistryAccess = { + kind: 'token-registry-access' + fee: fq.Fee +} + +export type TokenTransfer = { + kind: 'token-transfer' + tokenPool: Address + fee: fq.Fee +} + +export type Finalized = { + kind: 'finalized' +} + export type Config = { feeQuoter: Address + // Null when the send carries no token transfers. + tokenRegistry?: Address | null } export type Execute = { @@ -85,12 +109,16 @@ export const builder = (() => { const addresses: CellCodec = { encode: (data: Addresses): Builder => { - return beginCell().storeAddress(data.onramp).storeAddress(data.feeQuoter) + return beginCell() + .storeAddress(data.onramp) + .storeAddress(data.feeQuoter) + .storeAddress(data.tokenRegistry ?? null) }, load: (src: Slice): Addresses => { return { onramp: src.loadAddress(), feeQuoter: src.loadAddress(), + tokenRegistry: src.loadMaybeAddress(), } }, } @@ -99,31 +127,66 @@ export const builder = (() => { encode: function (data: State): Builder { switch (data.kind) { case 'initialized': - return beginCell().storeUint(0, 1) + return beginCell().storeUint(0b000, 3).storeRef(beginCell().endCell()) case 'on-going-fee-validation': - return beginCell().storeUint(1, 1) + return beginCell().storeUint(0b001, 3).storeRef(beginCell().endCell()) + case 'token-registry-access': + return beginCell() + .storeUint(0b010, 3) + .storeRef(fq.builder.data.fee.encode(data.fee).endCell()) + case 'token-transfer': + return beginCell() + .storeUint(0b011, 3) + .storeRef( + beginCell() + .storeAddress(data.tokenPool) + .storeBuilder(fq.builder.data.fee.encode(data.fee)) + .endCell(), + ) + case 'finalized': + return beginCell().storeUint(0b100, 3).storeRef(beginCell().endCell()) } }, load: function (src: Slice): State { - const kind = src.loadUint(1) - switch (kind) { - case 0: + const tag = src.loadUint(3) + switch (tag) { + case 0b000: + src.loadRef() return { kind: 'initialized' } - case 1: + case 0b001: + src.loadRef() return { kind: 'on-going-fee-validation' } + case 0b010: { + const feeSlice = src.loadRef().beginParse() + return { kind: 'token-registry-access', fee: fq.builder.data.fee.load(feeSlice) } + } + case 0b011: { + const inner = src.loadRef().beginParse() + return { + kind: 'token-transfer', + tokenPool: inner.loadAddress(), + fee: fq.builder.data.fee.load(inner), + } + } + case 0b100: + src.loadRef() + return { kind: 'finalized' } default: - throw new Error(`Unknown State kind: ${kind}`) + throw new Error(`Unknown State tag: ${tag}`) } }, } const config: CellCodec = { encode: (data: Config): Builder => { - return beginCell().storeAddress(data.feeQuoter) + return beginCell() + .storeAddress(data.feeQuoter) + .storeAddress(data.tokenRegistry ?? null) }, load: (src: Slice): Config => { return { feeQuoter: src.loadAddress(), + tokenRegistry: src.loadMaybeAddress(), } }, } @@ -193,6 +256,8 @@ export const opcodes = { execute: 0xaf3c62b3, messageValidated: fq.opcodes.out.messageValidated, messageValidationFailed: fq.opcodes.out.messageValidationFailed, + tokenRegistryReturnTokenInfo: 0xddccddb5, + mockTokenPoolNotifySuccessfulLockOrBurn: 0x7adb20bb, }, } diff --git a/contracts/wrappers/ccip/MockTokenPool.ts b/contracts/wrappers/ccip/MockTokenPool.ts new file mode 100644 index 000000000..ea60027f2 --- /dev/null +++ b/contracts/wrappers/ccip/MockTokenPool.ts @@ -0,0 +1,52 @@ +import { + Address, + beginCell, + Cell, + Contract, + contractAddress, + ContractProvider, + Sender, + SendMode, +} from '@ton/core' +import { compile } from '@ton/blueprint' + +export const ARTIFACT_NAME = 'ccip.test.mockTokenPool' + +// The MockTokenPool contract declares no storage, so its data cell is empty. +export class MockTokenPool implements Contract { + constructor( + readonly address: Address, + readonly init?: { code: Cell; data: Cell }, + ) {} + + static createFromAddress(address: Address) { + return new MockTokenPool(address) + } + + static createFromConfig(code: Cell, workchain = 0) { + const data = beginCell().endCell() + const init = { code, data } + return new MockTokenPool(contractAddress(workchain, init), init) + } + + static code(): Promise { + return compile(ARTIFACT_NAME) + } + + async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { + await provider.internal(via, { + value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: Cell.EMPTY, + }) + } +} + +export const opcodes = { + in: { + lockOrBurn: 0x7dd8f942, + }, + out: { + notifySuccessfulLockOrBurn: 0x7adb20bb, + }, +} diff --git a/contracts/wrappers/ccip/OnRamp.ts b/contracts/wrappers/ccip/OnRamp.ts index 67cc76628..8d6964294 100644 --- a/contracts/wrappers/ccip/OnRamp.ts +++ b/contracts/wrappers/ccip/OnRamp.ts @@ -42,6 +42,8 @@ export type OnRampStorage = { config: DynamicConfig destChainConfigs: Dictionary executor: ExecutorDeployment + // Address of the TokenRegistry queried during token transfers. Null when not configured. + tokenRegistry?: Address | null } export type ExecutorDeployment = { @@ -229,6 +231,7 @@ export const builder = (() => { .storeRef(dynamicConfig.encode(data.config).asCell()) .storeDict(data.destChainConfigs) .storeBuilder(executor.encode(data.executor)) + .storeAddress(data.tokenRegistry ?? null) }, load: function (src: Slice): OnRampStorage { const id = src.loadUintBig(32) @@ -237,6 +240,7 @@ export const builder = (() => { const config = dynamicConfig.load(src.loadRef().beginParse()) const destChainConfigs = src.loadDict(Dictionary.Keys.BigUint(64), Dictionary.Values.Cell()) const executorData = executor.load(src) + const tokenRegistry = src.loadMaybeAddress() return { id, ownable, @@ -244,6 +248,7 @@ export const builder = (() => { config, destChainConfigs, executor: executorData, + tokenRegistry, } }, } @@ -646,6 +651,7 @@ export const opcodes = { get messageValidationFailed() { return fq.opcodes.out.messageValidationFailed }, + executorRequestsLockOrBurn: 0x9be1fb61, executorFinishedSuccessfully: 0xcfa6b336, executorFinishedWithError: 0xc4068e21, setDynamicConfig: 0xa178c62e, diff --git a/contracts/wrappers/ccip/Router.ts b/contracts/wrappers/ccip/Router.ts index 19118aabb..a62e51c7f 100644 --- a/contracts/wrappers/ccip/Router.ts +++ b/contracts/wrappers/ccip/Router.ts @@ -72,6 +72,7 @@ export const opcodes = { messageSent: 0x6513f8e1, messageRejected: 0x8ae25114, getValidatedFee: 0x4dd6aa82, + lockOrBurn: 0x6f2d00df, rmnOwnableMessage: 0xaf7a9ac6, }, out: { diff --git a/contracts/wrappers/ccip/TokenRegistry.ts b/contracts/wrappers/ccip/TokenRegistry.ts new file mode 100644 index 000000000..01f99b65a --- /dev/null +++ b/contracts/wrappers/ccip/TokenRegistry.ts @@ -0,0 +1,66 @@ +import { + Address, + beginCell, + Cell, + Contract, + contractAddress, + ContractProvider, + Sender, + SendMode, +} from '@ton/core' +import { compile } from '@ton/blueprint' + +export const ARTIFACT_NAME = 'ccip.TokenRegistry' + +export type TokenInfo = { + tokenPool: Address + minterAddress: Address + enabled: boolean +} + +export type TokenRegistryStorage = { + info: TokenInfo +} + +export function storageToCell(data: TokenRegistryStorage): Cell { + return beginCell() + .storeAddress(data.info.tokenPool) + .storeAddress(data.info.minterAddress) + .storeBit(data.info.enabled) + .endCell() +} + +export class TokenRegistry implements Contract { + constructor( + readonly address: Address, + readonly init?: { code: Cell; data: Cell }, + ) {} + + static createFromAddress(address: Address) { + return new TokenRegistry(address) + } + + static createFromConfig(config: TokenRegistryStorage, code: Cell, workchain = 0) { + const data = storageToCell(config) + const init = { code, data } + return new TokenRegistry(contractAddress(workchain, init), init) + } + + static code(): Promise { + return compile(ARTIFACT_NAME) + } + + async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { + await provider.internal(via, { + value, + sendMode: SendMode.PAY_GAS_SEPARATELY, + body: Cell.EMPTY, + }) + } +} + +export const opcodes = { + out: { + returnTokenInfo: 0xddccddb5, + }, +} diff --git a/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts b/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts index 3a41d7b76..7313b855f 100644 --- a/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts +++ b/contracts/wrappers/gen/ccip/CCIPSendExecutor.ts @@ -277,18 +277,21 @@ export const CCIPSendExecutor_Data = { > struct CCIPSendExecutor_Addresses { > onramp: address > feeQuoter: address + > tokenRegistry: address? > } */ export interface CCIPSendExecutor_Addresses { readonly $: 'CCIPSendExecutor_Addresses' onramp: c.Address feeQuoter: c.Address + tokenRegistry: c.Address | null } export const CCIPSendExecutor_Addresses = { create(args: { onramp: c.Address feeQuoter: c.Address + tokenRegistry: c.Address | null }): CCIPSendExecutor_Addresses { return { $: 'CCIPSendExecutor_Addresses', @@ -300,11 +303,13 @@ export const CCIPSendExecutor_Addresses = { $: 'CCIPSendExecutor_Addresses', onramp: s.loadAddress(), feeQuoter: s.loadAddress(), + tokenRegistry: s.loadMaybeAddress(), } }, store(self: CCIPSendExecutor_Addresses, b: c.Builder): void { b.storeAddress(self.onramp); b.storeAddress(self.feeQuoter); + b.storeAddress(self.tokenRegistry); }, toCell(self: CCIPSendExecutor_Addresses): c.Cell { return makeCellFrom(self, CCIPSendExecutor_Addresses.store); @@ -312,32 +317,44 @@ export const CCIPSendExecutor_Addresses = { } /** - > type CCIPSendExecutor_State = Cell | Cell | Cell + > type CCIPSendExecutor_State = Cell | Cell | Cell | Cell | Cell */ export type CCIPSendExecutor_State = | { $: 'Cell', value: CellRef } | { $: 'Cell', value: CellRef } + | { $: 'Cell', value: CellRef } + | { $: 'Cell', value: CellRef } | { $: 'Cell', value: CellRef } export const CCIPSendExecutor_State = { fromSlice(s: c.Slice): CCIPSendExecutor_State { - return lookupPrefixAndEat(s, 0b00, 2) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_Initialized.fromSlice) } : - lookupPrefixAndEat(s, 0b01, 2) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_OnGoingFeeValidation.fromSlice) } : - lookupPrefixAndEat(s, 0b10, 2) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_Finalized.fromSlice) } : + return lookupPrefixAndEat(s, 0b000, 3) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_Initialized.fromSlice) } : + lookupPrefixAndEat(s, 0b001, 3) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_OnGoingFeeValidation.fromSlice) } : + lookupPrefixAndEat(s, 0b010, 3) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_TokenRegistryAccess.fromSlice) } : + lookupPrefixAndEat(s, 0b011, 3) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_TokenTransfer.fromSlice) } : + lookupPrefixAndEat(s, 0b100, 3) ? { $: 'Cell', value: loadCellRef(s, CCIPSendExecutor_State_Finalized.fromSlice) } : throwNonePrefixMatch('CCIPSendExecutor_State'); }, store(self: CCIPSendExecutor_State, b: c.Builder): void { switch (self.$) { case 'Cell': - b.storeUint(0b00, 2); + b.storeUint(0b000, 3); storeCellRef(self.value, b, CCIPSendExecutor_State_Initialized.store); break; case 'Cell': - b.storeUint(0b01, 2); + b.storeUint(0b001, 3); storeCellRef(self.value, b, CCIPSendExecutor_State_OnGoingFeeValidation.store); break; + case 'Cell': + b.storeUint(0b010, 3); + storeCellRef(self.value, b, CCIPSendExecutor_State_TokenRegistryAccess.store); + break; + case 'Cell': + b.storeUint(0b011, 3); + storeCellRef(self.value, b, CCIPSendExecutor_State_TokenTransfer.store); + break; case 'Cell': - b.storeUint(0b10, 2); + b.storeUint(0b100, 3); storeCellRef(self.value, b, CCIPSendExecutor_State_Finalized.store); break; } @@ -399,6 +416,77 @@ export const CCIPSendExecutor_State_OnGoingFeeValidation = { } } +/** + > struct CCIPSendExecutor_State_TokenRegistryAccess { + > fee: Fee + > } + */ +export interface CCIPSendExecutor_State_TokenRegistryAccess { + readonly $: 'CCIPSendExecutor_State_TokenRegistryAccess' + fee: Fee +} + +export const CCIPSendExecutor_State_TokenRegistryAccess = { + create(args: { + fee: Fee + }): CCIPSendExecutor_State_TokenRegistryAccess { + return { + $: 'CCIPSendExecutor_State_TokenRegistryAccess', + ...args + } + }, + fromSlice(s: c.Slice): CCIPSendExecutor_State_TokenRegistryAccess { + return { + $: 'CCIPSendExecutor_State_TokenRegistryAccess', + fee: Fee.fromSlice(s), + } + }, + store(self: CCIPSendExecutor_State_TokenRegistryAccess, b: c.Builder): void { + Fee.store(self.fee, b); + }, + toCell(self: CCIPSendExecutor_State_TokenRegistryAccess): c.Cell { + return makeCellFrom(self, CCIPSendExecutor_State_TokenRegistryAccess.store); + } +} + +/** + > struct CCIPSendExecutor_State_TokenTransfer { + > tokenPool: address + > fee: Fee + > } + */ +export interface CCIPSendExecutor_State_TokenTransfer { + readonly $: 'CCIPSendExecutor_State_TokenTransfer' + tokenPool: c.Address + fee: Fee +} + +export const CCIPSendExecutor_State_TokenTransfer = { + create(args: { + tokenPool: c.Address + fee: Fee + }): CCIPSendExecutor_State_TokenTransfer { + return { + $: 'CCIPSendExecutor_State_TokenTransfer', + ...args + } + }, + fromSlice(s: c.Slice): CCIPSendExecutor_State_TokenTransfer { + return { + $: 'CCIPSendExecutor_State_TokenTransfer', + tokenPool: s.loadAddress(), + fee: Fee.fromSlice(s), + } + }, + store(self: CCIPSendExecutor_State_TokenTransfer, b: c.Builder): void { + b.storeAddress(self.tokenPool); + Fee.store(self.fee, b); + }, + toCell(self: CCIPSendExecutor_State_TokenTransfer): c.Cell { + return makeCellFrom(self, CCIPSendExecutor_State_TokenTransfer.store); + } +} + /** > struct CCIPSendExecutor_State_Finalized { > } @@ -428,16 +516,19 @@ export const CCIPSendExecutor_State_Finalized = { /** > struct CCIPSendExecutor_Config { > feeQuoter: address + > tokenRegistry: address? > } */ export interface CCIPSendExecutor_Config { readonly $: 'CCIPSendExecutor_Config' feeQuoter: c.Address + tokenRegistry: c.Address | null } export const CCIPSendExecutor_Config = { create(args: { feeQuoter: c.Address + tokenRegistry: c.Address | null }): CCIPSendExecutor_Config { return { $: 'CCIPSendExecutor_Config', @@ -448,10 +539,12 @@ export const CCIPSendExecutor_Config = { return { $: 'CCIPSendExecutor_Config', feeQuoter: s.loadAddress(), + tokenRegistry: s.loadMaybeAddress(), } }, store(self: CCIPSendExecutor_Config, b: c.Builder): void { b.storeAddress(self.feeQuoter); + b.storeAddress(self.tokenRegistry); }, toCell(self: CCIPSendExecutor_Config): c.Cell { return makeCellFrom(self, CCIPSendExecutor_Config.store); @@ -626,6 +719,58 @@ export const OnRamp_Send = { } } +/** + > struct (0x9be1fb61) OnRamp_ExecutorRequestsLockOrBurn { + > tokenAmount: TokenAmount + > tokenPool: address + > destChainSelector: uint64 + > executorID: CCIPSendExecutor_ID + > } + */ +export interface OnRamp_ExecutorRequestsLockOrBurn { + readonly $: 'OnRamp_ExecutorRequestsLockOrBurn' + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID +} + +export const OnRamp_ExecutorRequestsLockOrBurn = { + PREFIX: 0x9be1fb61, + + create(args: { + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID + }): OnRamp_ExecutorRequestsLockOrBurn { + return { + $: 'OnRamp_ExecutorRequestsLockOrBurn', + ...args + } + }, + fromSlice(s: c.Slice): OnRamp_ExecutorRequestsLockOrBurn { + loadAndCheckPrefix32(s, 0x9be1fb61, 'OnRamp_ExecutorRequestsLockOrBurn'); + return { + $: 'OnRamp_ExecutorRequestsLockOrBurn', + tokenAmount: TokenAmount.fromSlice(s), + tokenPool: s.loadAddress(), + destChainSelector: s.loadUintBig(64), + executorID: CCIPSendExecutor_ID.fromSlice(s), + } + }, + store(self: OnRamp_ExecutorRequestsLockOrBurn, b: c.Builder): void { + b.storeUint(0x9be1fb61, 32); + TokenAmount.store(self.tokenAmount, b); + b.storeAddress(self.tokenPool); + b.storeUint(self.destChainSelector, 64); + CCIPSendExecutor_ID.store(self.executorID, b); + }, + toCell(self: OnRamp_ExecutorRequestsLockOrBurn): c.Cell { + return makeCellFrom(self, OnRamp_ExecutorRequestsLockOrBurn.store); + } +} + /** > struct (0xcfa6b336) OnRamp_ExecutorFinishedSuccessfully { > executorID: CCIPSendExecutor_ID @@ -857,44 +1002,6 @@ export const Fee = { } } -/** - > struct Metadata { - > sender: address - > value: coins - > } - */ -export interface Metadata { - readonly $: 'Metadata' - sender: c.Address - value: coins -} - -export const Metadata = { - create(args: { - sender: c.Address - value: coins - }): Metadata { - return { - $: 'Metadata', - ...args - } - }, - fromSlice(s: c.Slice): Metadata { - return { - $: 'Metadata', - sender: s.loadAddress(), - value: s.loadCoins(), - } - }, - store(self: Metadata, b: c.Builder): void { - b.storeAddress(self.sender); - b.storeCoins(self.value); - }, - toCell(self: Metadata): c.Cell { - return makeCellFrom(self, Metadata.store); - } -} - /** > type CrossChainAddress = slice */ @@ -1103,6 +1210,184 @@ export const TokenAmount = { } } +/** + > struct (0xdd5d5127) TokenRegistry_GetTokenInfo { + > } + */ +export interface TokenRegistry_GetTokenInfo { + readonly $: 'TokenRegistry_GetTokenInfo' +} + +export const TokenRegistry_GetTokenInfo = { + PREFIX: 0xdd5d5127, + + create(): TokenRegistry_GetTokenInfo { + return { + $: 'TokenRegistry_GetTokenInfo', + } + }, + fromSlice(s: c.Slice): TokenRegistry_GetTokenInfo { + loadAndCheckPrefix32(s, 0xdd5d5127, 'TokenRegistry_GetTokenInfo'); + return { + $: 'TokenRegistry_GetTokenInfo', + } + }, + store(self: TokenRegistry_GetTokenInfo, b: c.Builder): void { + b.storeUint(0xdd5d5127, 32); + }, + toCell(self: TokenRegistry_GetTokenInfo): c.Cell { + return makeCellFrom(self, TokenRegistry_GetTokenInfo.store); + } +} + +/** + > struct (0xddccddb5) TokenRegistry_ReturnTokenInfo { + > tokenInfo: TokenRegistry_TokenInfo + > } + */ +export interface TokenRegistry_ReturnTokenInfo { + readonly $: 'TokenRegistry_ReturnTokenInfo' + tokenInfo: TokenRegistry_TokenInfo +} + +export const TokenRegistry_ReturnTokenInfo = { + PREFIX: 0xddccddb5, + + create(args: { + tokenInfo: TokenRegistry_TokenInfo + }): TokenRegistry_ReturnTokenInfo { + return { + $: 'TokenRegistry_ReturnTokenInfo', + ...args + } + }, + fromSlice(s: c.Slice): TokenRegistry_ReturnTokenInfo { + loadAndCheckPrefix32(s, 0xddccddb5, 'TokenRegistry_ReturnTokenInfo'); + return { + $: 'TokenRegistry_ReturnTokenInfo', + tokenInfo: TokenRegistry_TokenInfo.fromSlice(s), + } + }, + store(self: TokenRegistry_ReturnTokenInfo, b: c.Builder): void { + b.storeUint(0xddccddb5, 32); + TokenRegistry_TokenInfo.store(self.tokenInfo, b); + }, + toCell(self: TokenRegistry_ReturnTokenInfo): c.Cell { + return makeCellFrom(self, TokenRegistry_ReturnTokenInfo.store); + } +} + +/** + > struct (0x7adb20bb) MockTokenPool_NotifySuccessfulLockOrBurn { + > } + */ +export interface MockTokenPool_NotifySuccessfulLockOrBurn { + readonly $: 'MockTokenPool_NotifySuccessfulLockOrBurn' +} + +export const MockTokenPool_NotifySuccessfulLockOrBurn = { + PREFIX: 0x7adb20bb, + + create(): MockTokenPool_NotifySuccessfulLockOrBurn { + return { + $: 'MockTokenPool_NotifySuccessfulLockOrBurn', + } + }, + fromSlice(s: c.Slice): MockTokenPool_NotifySuccessfulLockOrBurn { + loadAndCheckPrefix32(s, 0x7adb20bb, 'MockTokenPool_NotifySuccessfulLockOrBurn'); + return { + $: 'MockTokenPool_NotifySuccessfulLockOrBurn', + } + }, + store(self: MockTokenPool_NotifySuccessfulLockOrBurn, b: c.Builder): void { + b.storeUint(0x7adb20bb, 32); + }, + toCell(self: MockTokenPool_NotifySuccessfulLockOrBurn): c.Cell { + return makeCellFrom(self, MockTokenPool_NotifySuccessfulLockOrBurn.store); + } +} + +/** + > struct Metadata { + > sender: address + > value: coins + > } + */ +export interface Metadata { + readonly $: 'Metadata' + sender: c.Address + value: coins +} + +export const Metadata = { + create(args: { + sender: c.Address + value: coins + }): Metadata { + return { + $: 'Metadata', + ...args + } + }, + fromSlice(s: c.Slice): Metadata { + return { + $: 'Metadata', + sender: s.loadAddress(), + value: s.loadCoins(), + } + }, + store(self: Metadata, b: c.Builder): void { + b.storeAddress(self.sender); + b.storeCoins(self.value); + }, + toCell(self: Metadata): c.Cell { + return makeCellFrom(self, Metadata.store); + } +} + +/** + > struct TokenRegistry_TokenInfo { + > tokenPool: address + > minterAddress: address + > enabled: bool + > } + */ +export interface TokenRegistry_TokenInfo { + readonly $: 'TokenRegistry_TokenInfo' + tokenPool: c.Address + minterAddress: c.Address + enabled: boolean +} + +export const TokenRegistry_TokenInfo = { + create(args: { + tokenPool: c.Address + minterAddress: c.Address + enabled: boolean + }): TokenRegistry_TokenInfo { + return { + $: 'TokenRegistry_TokenInfo', + ...args + } + }, + fromSlice(s: c.Slice): TokenRegistry_TokenInfo { + return { + $: 'TokenRegistry_TokenInfo', + tokenPool: s.loadAddress(), + minterAddress: s.loadAddress(), + enabled: s.loadBoolean(), + } + }, + store(self: TokenRegistry_TokenInfo, b: c.Builder): void { + b.storeAddress(self.tokenPool); + b.storeAddress(self.minterAddress); + b.storeBit(self.enabled); + }, + toCell(self: TokenRegistry_TokenInfo): c.Cell { + return makeCellFrom(self, TokenRegistry_TokenInfo.store); + } +} + // ———————————————————————————————————————————— // class CCIPSendExecutor // @@ -1142,12 +1427,15 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class CCIPSendExecutor implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECFwEAA+cAART/APSkE/S88sgLAQIBYgIDAgLPBAUCAUgTFARdPiRjo/THzHXLCOkt/q0MeMC8j/gINcsJXnjFZzjAtcsIP0wG6TjAtcsJeeFWHyAGBwgJA/cW4IJMS0AggnZBcCCEAVdSoCCEATjOICCC8FNwLYJoKCgIqAlvOMC+ACIVHh2U4cEyMvfz5Nz5k8KE8z6UgH6AszPhoDMye1UI9D6SPpIMdHIz5M+mszaKc8L31AD+gLLXybPFFJQ+lIk+gLJyM+FiBL6UnHPC27MyYMGgEBIRA/ztRNDT39csJufMnhTyv9T6SPoA1NcsAZQwgQCHjhXXLAOUMIEAiJvXLAUxkvI/4YEAieLigUWIgQCIWLry9IhUdUNTVATIy9/Pk3PmTwoTzPpSAfoCzM+GgMzJ7VTQ+kj6SDHRyM+TEBo4hhXL34FFjM8L/xPM+lIB+gLJyIkSCgsC/jHXLCbnzJ4U8r/U+kj6ANdM0PpI0e1E0PpI1wvfAcj6UhL6UsmBRYn4kvgoxwXy9IFFi/iXghAFXUqAghAE4ziAggvBTcC2CaC+8vQg0PpIMfpI0YIQBBzbQIsIyM+R0lv9WijPFM7JyM+FiBP6UgH6AnHPC2rMyXH7AIhUclQSDADOMe1E0NPf1ywm58yeFPK/1PpI+gDU1ywBlddMgQCHjhfXLAOV10yBAIic1ywFkvI/4ddMgQCJ4uKBRYiBAIhYuvL0gUWJItD6SDH6SNH4kscF8vQG+gDTX9QQiRB4EGcQVhBF8AFfBgEU4wIwhA8BxwDy9A0AAWIAHs8WEvpScc8LbszJgwb7AAA+JjY2NjYFyMvfz5Nz5k8KFMwS+lIB+gLMz4WAzMntVAP+Me1E0NPf1ywm58yeFPK/1PpI+gDU1ywBlDCBAIeOFdcsA5QwgQCIm9csBTGS8j/hgQCJ4uKBRYiBAIhYuvL0gUWJIdD6SDH6SNH4kscF8vQF1wv/iFR1Q1NZBMjL38+Tc+ZPChPM+lIB+gLMz4aAzMntVCXQNgX6SPpIMdHIiRIODwAIxAaOIQBWzxYlzwvfNVBUy/8izxQyUgL6UjEi+gJsEsnIz4WIEvpScc8LbszJgwb7AAGqW4hUdlRTZQTIy9/Pk3PmTwoTzPpSAfoCzM+GgMzJ7VQh0PpI+kgx0cjPkxAaOIYnzwvfgUWKzwv/Js8UUlD6UiT6AsnIz4WIEvpScc8LbszJgwb7ABIABPsAAAACASAVFgALuGhYEAsoAGG2K/GhI2NLc1lzG0MLS3Fzo3txcxsbS4FyGhpKgpsrcyIrwysbq6N7lBFqYlxsXGMQABm1xRAosRQEEIH3flCQ'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { + 'Common_Error.CrossChainAddressOutOfRange': 5, + 'Utils_Error.InvalidData': 13500, 'CCIPSendExecutor_Error.StateNotExpected': 17800, 'CCIPSendExecutor_Error.Unauthorized': 17801, 'CCIPSendExecutor_Error.InsufficientFee': 17803, + 'CCIPSendExecutor_Error.TokenNotEnabled': 17805, } readonly address: c.Address @@ -1218,6 +1506,17 @@ export class CCIPSendExecutor implements c.Contract { ); } + static createCellOfTokenRegistryReturnTokenInfo(body: { + tokenInfo: TokenRegistry_TokenInfo + }) { + return TokenRegistry_ReturnTokenInfo.toCell(TokenRegistry_ReturnTokenInfo.create(body)); + } + + static createCellOfMockTokenPoolNotifySuccessfulLockOrBurn(body: { + }) { + return MockTokenPool_NotifySuccessfulLockOrBurn.toCell(MockTokenPool_NotifySuccessfulLockOrBurn.create()); + } + async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -1271,6 +1570,25 @@ export class CCIPSendExecutor implements c.Contract { }); } + async sendTokenRegistryReturnTokenInfo(provider: ContractProvider, via: Sender, msgValue: coins, body: { + tokenInfo: TokenRegistry_TokenInfo + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: TokenRegistry_ReturnTokenInfo.toCell(TokenRegistry_ReturnTokenInfo.create(body)), + ...extraOptions + }); + } + + async sendMockTokenPoolNotifySuccessfulLockOrBurn(provider: ContractProvider, via: Sender, msgValue: coins, body: { + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: MockTokenPool_NotifySuccessfulLockOrBurn.toCell(MockTokenPool_NotifySuccessfulLockOrBurn.create()), + ...extraOptions + }); + } + async getTypeAndVersion(provider: ContractProvider): Promise<[ c.Slice, c.Slice, diff --git a/contracts/wrappers/gen/ccip/FeeQuoter.ts b/contracts/wrappers/gen/ccip/FeeQuoter.ts index dbcab4859..82a0a86d9 100644 --- a/contracts/wrappers/gen/ccip/FeeQuoter.ts +++ b/contracts/wrappers/gen/ccip/FeeQuoter.ts @@ -1955,7 +1955,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class FeeQuoter 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, @@ -1965,7 +1965,6 @@ export class FeeQuoter implements c.Contract { 'FeeQuoter_Error.GasLimitTooHigh': 34401, 'FeeQuoter_Error.ExtraArgOutOfOrderExecutionMustBeTrue': 34402, 'FeeQuoter_Error.InvalidExtraArgsData': 34403, - 'FeeQuoter_Error.UnsupportedNumberOfTokens': 34404, 'FeeQuoter_Error.InvalidSuiReceiverAddress': 34407, 'FeeQuoter_Error.InvalidSVMReceiverAddress': 34408, 'FeeQuoter_Error.TooManySuiExtraArgsReceiverObjectIds': 34410, diff --git a/contracts/wrappers/gen/ccip/OnRamp.ts b/contracts/wrappers/gen/ccip/OnRamp.ts index 4cf96d8fe..262cd43cf 100644 --- a/contracts/wrappers/gen/ccip/OnRamp.ts +++ b/contracts/wrappers/gen/ccip/OnRamp.ts @@ -1058,6 +1058,58 @@ export const Router_MessageRejected = { } } +/** + > struct (0x6f2d00df) Router_LockOrBurn { + > tokenPool: address + > tokenAmount: TokenAmount + > destChainSelector: uint64 + > executorAddress: address + > } + */ +export interface Router_LockOrBurn { + readonly $: 'Router_LockOrBurn' + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address +} + +export const Router_LockOrBurn = { + PREFIX: 0x6f2d00df, + + create(args: { + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address + }): Router_LockOrBurn { + return { + $: 'Router_LockOrBurn', + ...args + } + }, + fromSlice(s: c.Slice): Router_LockOrBurn { + loadAndCheckPrefix32(s, 0x6f2d00df, 'Router_LockOrBurn'); + return { + $: 'Router_LockOrBurn', + tokenPool: s.loadAddress(), + tokenAmount: TokenAmount.fromSlice(s), + destChainSelector: s.loadUintBig(64), + executorAddress: s.loadAddress(), + } + }, + store(self: Router_LockOrBurn, b: c.Builder): void { + b.storeUint(0x6f2d00df, 32); + b.storeAddress(self.tokenPool); + TokenAmount.store(self.tokenAmount, b); + b.storeUint(self.destChainSelector, 64); + b.storeAddress(self.executorAddress); + }, + toCell(self: Router_LockOrBurn): c.Cell { + return makeCellFrom(self, Router_LockOrBurn.store); + } +} + /** > struct (0x7496ff56) FeeQuoter_GetValidatedFee { > msg: Cell @@ -1633,7 +1685,7 @@ export const TVM2AnyRampMessage = { > receiver: Cell > data: cell > extraArgs: cell - > tokenAmounts: cell + > tokenAmounts: SnakedCell > feeToken: address > feeTokenAmount: uint256 > } @@ -1643,7 +1695,7 @@ export interface TVM2AnyRampMessageBody { receiver: CellRef data: c.Cell extraArgs: c.Cell - tokenAmounts: c.Cell + tokenAmounts: SnakedCell feeToken: c.Address feeTokenAmount: uint256 } @@ -1653,7 +1705,7 @@ export const TVM2AnyRampMessageBody = { receiver: CellRef data: c.Cell extraArgs: c.Cell - tokenAmounts: c.Cell + tokenAmounts: SnakedCell feeToken: c.Address feeTokenAmount: uint256 }): TVM2AnyRampMessageBody { @@ -1694,6 +1746,7 @@ export const TVM2AnyRampMessageBody = { > config: Cell > destChainConfigs: map > executor: ExecutorDeployment + > tokenRegistry: address? > } */ export interface OnRamp_Storage { @@ -1704,6 +1757,7 @@ export interface OnRamp_Storage { config: CellRef destChainConfigs: c.Dictionary executor: ExecutorDeployment + tokenRegistry: c.Address | null } export const OnRamp_Storage = { @@ -1714,6 +1768,7 @@ export const OnRamp_Storage = { config: CellRef destChainConfigs: c.Dictionary executor: ExecutorDeployment + tokenRegistry: c.Address | null }): OnRamp_Storage { return { $: 'OnRamp_Storage', @@ -1729,6 +1784,7 @@ export const OnRamp_Storage = { config: loadCellRef(s, OnRamp_DynamicConfig.fromSlice), destChainConfigs: c.Dictionary.load(c.Dictionary.Keys.BigUint(64), createDictionaryValue(OnRamp_DestChainConfig.fromSlice, OnRamp_DestChainConfig.store), s), executor: ExecutorDeployment.fromSlice(s), + tokenRegistry: s.loadMaybeAddress(), } }, store(self: OnRamp_Storage, b: c.Builder): void { @@ -1738,6 +1794,7 @@ export const OnRamp_Storage = { storeCellRef(self.config, b, OnRamp_DynamicConfig.store); b.storeDict(self.destChainConfigs, c.Dictionary.Keys.BigUint(64), createDictionaryValue(OnRamp_DestChainConfig.fromSlice, OnRamp_DestChainConfig.store)); ExecutorDeployment.store(self.executor, b); + b.storeAddress(self.tokenRegistry); }, toCell(self: OnRamp_Storage): c.Cell { return makeCellFrom(self, OnRamp_Storage.store); @@ -1812,6 +1869,58 @@ export const OnRamp_GetValidatedFee = { }, } +/** + > struct (0x9be1fb61) OnRamp_ExecutorRequestsLockOrBurn { + > tokenAmount: TokenAmount + > tokenPool: address + > destChainSelector: uint64 + > executorID: CCIPSendExecutor_ID + > } + */ +export interface OnRamp_ExecutorRequestsLockOrBurn { + readonly $: 'OnRamp_ExecutorRequestsLockOrBurn' + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID +} + +export const OnRamp_ExecutorRequestsLockOrBurn = { + PREFIX: 0x9be1fb61, + + create(args: { + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID + }): OnRamp_ExecutorRequestsLockOrBurn { + return { + $: 'OnRamp_ExecutorRequestsLockOrBurn', + ...args + } + }, + fromSlice(s: c.Slice): OnRamp_ExecutorRequestsLockOrBurn { + loadAndCheckPrefix32(s, 0x9be1fb61, 'OnRamp_ExecutorRequestsLockOrBurn'); + return { + $: 'OnRamp_ExecutorRequestsLockOrBurn', + tokenAmount: TokenAmount.fromSlice(s), + tokenPool: s.loadAddress(), + destChainSelector: s.loadUintBig(64), + executorID: CCIPSendExecutor_ID.fromSlice(s), + } + }, + store(self: OnRamp_ExecutorRequestsLockOrBurn, b: c.Builder): void { + b.storeUint(0x9be1fb61, 32); + TokenAmount.store(self.tokenAmount, b); + b.storeAddress(self.tokenPool); + b.storeUint(self.destChainSelector, 64); + CCIPSendExecutor_ID.store(self.executorID, b); + }, + toCell(self: OnRamp_ExecutorRequestsLockOrBurn): c.Cell { + return makeCellFrom(self, OnRamp_ExecutorRequestsLockOrBurn.store); + } +} + /** > struct OnRamp_GetValidatedFeeContext { > onrampContext: address @@ -2236,7 +2345,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class OnRamp 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, @@ -2281,6 +2390,7 @@ export class OnRamp implements c.Contract { config: CellRef destChainConfigs: c.Dictionary executor: ExecutorDeployment + tokenRegistry: c.Address | null }, deployedOptions?: DeployedAddrOptions) { const initialState = { code: deployedOptions?.overrideContractCode ?? OnRamp.CodeCell, @@ -2334,6 +2444,15 @@ export class OnRamp implements c.Contract { ); } + static createCellOfOnRampExecutorRequestsLockOrBurn(body: { + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID + }) { + return OnRamp_ExecutorRequestsLockOrBurn.toCell(OnRamp_ExecutorRequestsLockOrBurn.create(body)); + } + static createCellOfOnRampExecutorFinishedSuccessfully(body: { executorID: CCIPSendExecutor_ID fee: Fee @@ -2457,6 +2576,19 @@ export class OnRamp implements c.Contract { }); } + async sendOnRampExecutorRequestsLockOrBurn(provider: ContractProvider, via: Sender, msgValue: coins, body: { + tokenAmount: TokenAmount + tokenPool: c.Address + destChainSelector: uint64 + executorID: CCIPSendExecutor_ID + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: OnRamp_ExecutorRequestsLockOrBurn.toCell(OnRamp_ExecutorRequestsLockOrBurn.create(body)), + ...extraOptions + }); + } + async sendOnRampExecutorFinishedSuccessfully(provider: ContractProvider, via: Sender, msgValue: coins, body: { executorID: CCIPSendExecutor_ID fee: Fee diff --git a/contracts/wrappers/gen/ccip/Router.ts b/contracts/wrappers/gen/ccip/Router.ts index 104e45aa9..ee0cf53b1 100644 --- a/contracts/wrappers/gen/ccip/Router.ts +++ b/contracts/wrappers/gen/ccip/Router.ts @@ -1005,6 +1005,102 @@ export const ReceiveExecutorId = { } } +/** + > struct (0x7362d09c) Common_JettonTransferNotification { + > queryId: uint64 + > amount: coins + > sender: address + > forwardPayload: cell? + > } + */ +export interface Common_JettonTransferNotification { + readonly $: 'Common_JettonTransferNotification' + queryId: uint64 + amount: coins + sender: c.Address + forwardPayload: c.Cell | null +} + +export const Common_JettonTransferNotification = { + PREFIX: 0x7362d09c, + + create(args: { + queryId: uint64 + amount: coins + sender: c.Address + forwardPayload: c.Cell | null + }): Common_JettonTransferNotification { + return { + $: 'Common_JettonTransferNotification', + ...args + } + }, + fromSlice(s: c.Slice): Common_JettonTransferNotification { + loadAndCheckPrefix32(s, 0x7362d09c, 'Common_JettonTransferNotification'); + return { + $: 'Common_JettonTransferNotification', + queryId: s.loadUintBig(64), + amount: s.loadCoins(), + sender: s.loadAddress(), + forwardPayload: s.loadBoolean() ? s.loadRef() : null, + } + }, + store(self: Common_JettonTransferNotification, b: c.Builder): void { + b.storeUint(0x7362d09c, 32); + b.storeUint(self.queryId, 64); + b.storeCoins(self.amount); + b.storeAddress(self.sender); + storeTolkNullable(self.forwardPayload, b, + (v,b) => b.storeRef(v) + ); + }, + toCell(self: Common_JettonTransferNotification): c.Cell { + return makeCellFrom(self, Common_JettonTransferNotification.store); + } +} + +/** + > struct (0x7dd8f942) MockTokenPool_LockOrBurn { + > tokenAmount: TokenAmount + > notify: address + > } + */ +export interface MockTokenPool_LockOrBurn { + readonly $: 'MockTokenPool_LockOrBurn' + tokenAmount: TokenAmount + notify: c.Address +} + +export const MockTokenPool_LockOrBurn = { + PREFIX: 0x7dd8f942, + + create(args: { + tokenAmount: TokenAmount + notify: c.Address + }): MockTokenPool_LockOrBurn { + return { + $: 'MockTokenPool_LockOrBurn', + ...args + } + }, + fromSlice(s: c.Slice): MockTokenPool_LockOrBurn { + loadAndCheckPrefix32(s, 0x7dd8f942, 'MockTokenPool_LockOrBurn'); + return { + $: 'MockTokenPool_LockOrBurn', + tokenAmount: TokenAmount.fromSlice(s), + notify: s.loadAddress(), + } + }, + store(self: MockTokenPool_LockOrBurn, b: c.Builder): void { + b.storeUint(0x7dd8f942, 32); + TokenAmount.store(self.tokenAmount, b); + b.storeAddress(self.notify); + }, + toCell(self: MockTokenPool_LockOrBurn): c.Cell { + return makeCellFrom(self, MockTokenPool_LockOrBurn.store); + } +} + /** > struct OnRamps { > destChainSelectors: SnakedCell @@ -1807,6 +1903,58 @@ export const Router_GetValidatedFee = { }, } +/** + > struct (0x6f2d00df) Router_LockOrBurn { + > tokenPool: address + > tokenAmount: TokenAmount + > destChainSelector: uint64 + > executorAddress: address + > } + */ +export interface Router_LockOrBurn { + readonly $: 'Router_LockOrBurn' + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address +} + +export const Router_LockOrBurn = { + PREFIX: 0x6f2d00df, + + create(args: { + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address + }): Router_LockOrBurn { + return { + $: 'Router_LockOrBurn', + ...args + } + }, + fromSlice(s: c.Slice): Router_LockOrBurn { + loadAndCheckPrefix32(s, 0x6f2d00df, 'Router_LockOrBurn'); + return { + $: 'Router_LockOrBurn', + tokenPool: s.loadAddress(), + tokenAmount: TokenAmount.fromSlice(s), + destChainSelector: s.loadUintBig(64), + executorAddress: s.loadAddress(), + } + }, + store(self: Router_LockOrBurn, b: c.Builder): void { + b.storeUint(0x6f2d00df, 32); + b.storeAddress(self.tokenPool); + TokenAmount.store(self.tokenAmount, b); + b.storeUint(self.destChainSelector, 64); + b.storeAddress(self.executorAddress); + }, + toCell(self: Router_LockOrBurn): c.Cell { + return makeCellFrom(self, Router_LockOrBurn.store); + } +} + /** > struct Router_GetValidatedFeeContext { > routerContext: address @@ -2543,7 +2691,7 @@ function calculateDeployedAddress(code: c.Cell, data: c.Cell, options: DeployedA } export class Router implements c.Contract { - static CodeCell = c.Cell.fromBase64('te6ccgECTQEADjQAART/APSkE/S88sgLAQIBYgIDAgLGICECASAEBQIBIAYHAgEgGBkCASAICQIBIA4PAgEgCgsAG7XFEEAb4ZQEEIH3flCQAgEgDA0ATbBX40GmxpbmsuY2hhaW4udG9uLmNjaXAuUm91dGVygi1MS42LjGIAB3r4R2omg2gOmPmP0kGP0oGP0kGPoCkEAgekM30shHDKkBfSRogWRln4l9KWSoAbeBKJDAIHo+N9L0L4HAAE2sXXaiaGmPmP0kGP0oGP0kGPoA+gLBAG+GrMAgegc30Il5en0kaMACAnEQEQIBIBITABWmO9qJoaY+Y/SQYQAJpQsCBHcAgbOtu1E0NMfMfpIMfpQMfpIMfQB9AHXTND6SDH6UDH0BPQEMdFtIYMG9IZvpTKRAZ1SAm8CURKDBvR8b6Uy6DAxgAgEgFBUAe67+dqJoNoDpj5j9JBj9KBj9JBj6APoCkEAgekM30shHDKkBfSRogWRln4l9KWSoAbeBKJDAIHo+N9L0L4HAAgFmFhcAG6OvtRNDTHzH6SDH6UDCAEeiG7UTQ0x8x+kgx+lAx+kgx9AWCAN8MWYBA9A5voRLy9PpI0YASbrejtRNDTHzH6SDH6UDH6SDH0AfQB10zQ+kgx+lD0BDH0BDHRgCASAaGwIDeOAcHQIBIB4fAA+jMghA7msoAgBHoeO1E0NMfMfpIMfpQMfpIMfQB9AHXTND6SPpQMfQEMfQEMdGAFGy4HtRNDTHzH6SDH6UDH6SDH0AfQB10zQ+kgx+lAx9AT0BDHRAfADs4ABfscn7UTQ0x8x+kgx+lAx+kgx9AVtIYBA9IZvpTKRAZ1SAm8CURKAQPR8b6Uy6DAxgAgHNIiMCA6PSS0wCASAkJQIBSEZHAgEgJicCASBDRATfPiR4wIg1ywj7bOi7OMC1ywhi7RsrI5HMYIJMS0AggnZBcCCEAVdSoCCEATjOICCC8FNwLYJoKCgggDfFfiXWL7y9NM/0z/TByHBQfKFAaoC1xjU1PpQ10z4kviX8AXg1ywibrVUFOMC1ywhV9iN7ICgpKisC3w0+CdvECFukTGSNQTiA46pggDfDgHy8oIA3w1RI7wS8vQBcPsCgwaIyM+FCBP6UnHPC24SzMkB+wDgggDfDiHCAPL0ggDfDFMTufL0AoIA3w0EoSK8E/L0gECIyM+FCBT6Ulj6AnHPC2oSzMkB+wCBCQgLw0x8x1ywlmJNvjI5X1wu/+JLtRNDTHzH6SDH6UDH6SDH0BDH0BNQx0SLIy7/PUNcLP4IA3w0CgED0Dm+hEvL0+kjRggkSqIDIz4WIEvpSAfoCghAtzypDzwuKEsu/+lLJcPsA4NcsIUegs3zjAtcsIW55UhzjAvI/LCwE9DHtRNDTH/pI+lD6SPQE9ATXTPiSggDCiFEXxwXy9AfTPzHTAAGW1PpQgQCHlG1tWHDiAdMAAZbU+kiBAIiUbW1YcOIB0wABl9T6SDCBAIiUMG1tcOIGkjY24w0DkjMz4w2RW+MNBtD6SPpQ9AT0BDHRbSmAQPSGb6WQLS4vMABsMYIJQG9AghAFXUqAgglAb0CCCUBvQLYJoKCCCUBvQIIJQG9AtgmgggDfFfiXWL7y9NT4kvAEAvyOazHtRNAB+gDU+kgi0AXTHzH6SDH6UDH6SDH0BQXXLCGLtGys8r/TPzHXCz/4koIA3wxQJ4BA9A5voRfy9AX6SNEFggDfEgbHBRXy9MjPkniFV7JQA/oCzBLOycjPhQgS+lJxzwtuzMmAQPsA4NcsJWDuiXTjAtcsJ+NOKFwyMwBG1wu/+JLI+lLLv8nIz48YAASCEFjk9mTPC/dxzwthzMlw+wAAqCfQlCDHALOOKyDXSwGRMJuBNLwBwAHy9NdM0OLTPyhulguAQPRbMJooyPpSQAyAQPRD4groMMjPjxgABIIQfiTn3s8L93DPC2EYzBb6VMlw+wAQRQDAJNCUIMcAs444INdLAZEwm4E0vAHAAfL010zQ4tM/ggDfD1MogED0Dm+hEvL0+kjRggDfEFEXxwXy9AeAQPRbMAboMMjPjxgABIIQXNkW/M8L93DPC2EVzBP6Uslw+wASAIwh0JQgxwCzjiAg10sBkTCbgTS8AcAB8vTXTNDi0z8iyPpSQAWAQPRDA+gwyM+PGAAEghAwQGdhzwv3cM8LYRLM+lLJcPsAAUiK6FsDyPpSEvpU9AD0AMkFyMsfFPpSEvpU+lL0ABL0AMzJ7VQxACoB+kjRyEATgQEL9FEwURqAQPR8b6UA1DHtRNAB0//U+kgi0AXTHzH6SDH6UDH6SDH0BQXXLCGLtGys8r/TPzHXCz/4koIA3wxQJ4BA9A5voRfy9AX6SNEFggDfEgbHBRXy9MjPk7CPFYoTy//MEs7JyM+FiBL6UnHPC27MyYBA+wAE8uMC1ywg8q3ftI5bMYIA3xX4l4IJMS0AvvL01wu/+JLtRNAiyMu/z1DXCz8B0x8x+kgx+lAx+kgx9AH0BYIA3w1ZgED0Dm+hEvL0+kjRyM+FiPpSghAo9BZvzwuOEsu/+lLJgED7AODXLCeZxAI04wLXLCH4qdGM4wI0NTY3Af4x7UTQAdTTv/pI+gAwA9DT/9M/0wchwUHyhQGqAtcY1PQE0QjTHzH6SDH6UDH6SDH0AfQFI4IA3w0CgED0Dm+hEvL0+kjRggDfDviSWMcF8vTIz5LMSbfGFsu/E8v/yz8h10kgqTgC8kWrAiDBQfKFzwsHzhLME/QAycjPhYgTOAH+Me1E0NMf+kj6UPpI9AT0BNdMINAx+kj6UPQE9ATR+JKCAMKIURXHBfL0CtM/MddM0JQgxwCzjjkg10sBkTCbgTS8AcAB8vTXTNDi03/IVCAkgwb0UzDIz48YAASCEMzoMmPPC/dwzwthEst/yXD7AAHoMALI+lL6VFIQ9ABSgDkB/jHtRNDTH/pI+lD6SPQE9ATXTCDQMfpI+lD0BPQE0fiSggDCiFEVxwXy9ArTPzHXTNCUIMcAs443INdLAZEwm4E0vAHAAfL010zQ4tN/UhODBvRbMMjPjxgABIIQ2euDhc8L93DPC2ESy3/JcPsAAegwAsj6UvpUUhD0AFKA9AA6BPiJ1yeOKDHTP9cLf4IB64HtQ9j4ksjPhQj6UoIQIrqDs88LjhLLP8oAyYBA+wDg1ywle9TWNOMC1ywnmh/g3I4yMe1E0NMfMfpIMPiSggDCiALHBfL00z/6SPoA0wABkvoAkm0B4tcKAIIQO5rKAFVA8AHg1ywgVUCPbOMCOzw9PgAc+lIB+gJxzwtqzMlx+wAApvQAyQfIyx8W+lIU+lQS+lL0APQAEszJ7VQhgQEL9IJvpTKRAY4qIIIK+vCAyM+FCBL6UgH6AoIQTKG8s88LilIg9ADJcvsAIoEBC/R0b6Uy6F8DAKLJB8jLHxb6UhT6VBL6UvQA9AASzMntVCGBAQv0gm+lMpEBjiogggr68IDIz4UIEvpSAfoCghBMobyzzwuKUiD0AMly+wAigQEL9HRvpTLoXwMACAuVqk4AljHtRNDWH/pI+lD6SPQE9ATXTND6SPpQ9AT0BNH4khA0RAvwAo4gAcj6UvpU9AAX9ADJBcjOFPpSEvpU+lL0ABL0AMzJ7VTghA/y8AC6Me1E0NMfMfpIMPiSggDCiALHBfL00z8x10yT8QPoAJPxA+kAINoBI/sEI9DtHu1T7URAE9oh7VQh+QAB2gECyMzL/87JyM+PGAAEghCjO0mOzwv3cc8LYczJcPsAA/yJ1yeOUzHtRNAB0z/T/9M/+kgwBNMfMfpIMfpQMfpIMfQF+JKCAN8MWoBA9A5voRLy9PpI0QGCAN8SAscF8vTIz4UIE/pSghB40PIezwuOyz/L/8mAQPsA4NcsJFcSiKTjAjDtRNDWH/pI+lD4kkMwJfAC4wJfBIQPAccA8vQ/QEEACGUT+OEAqDHtRNAB0z/TP/pI1wv/BNMfMfpIMfpQMfpIMfQF+JKCAN8MUEKAQPQOb6ES8vT6SNECggDfEgPHBRLy9MjPhYj6UoIQWkXUNM8Ljss/y//JgED7AAAcNALIzhL6UhL6VM7J7VQAAAGpO2i7fvXLCeQ2+0MjkTXLCfPFPJUlFtw2zHhggDCiiNus/L0IYIAwooExwUT8vQgbQPXCz+LAgHIyz8V+lIS+lLJyM+HIBTOcc8LYRPMyXD7AOMNf4EUAVwhbpJbcOCCaQAAAAAAAAAAAAAAAAAAASKDBvQOb6Exklt/4AGDBvQOb6ExgAGZsEtM/+kgwggDCiFE0xwUT8vSCAMKJUyPHBbPy9CGLAsjPhyDOcM8LYRLLPxL6Uslw+wAC9ztRNBTM9AC0x8x+kgx+lAx+kj0BQPXLCGLtGys8r/WP9M/0wchwUHyhQGqAtcY1NT6UFJagED0Dm+hjiVfCsjPk7CPFYqCAN8Mzwv/E8zOycjPhYgS+lJxzwtuzMmAQPsA4TxulBBnXwfjDQP6SNHIz5JwszH6FMz6Us6BISQH3O1E0NMfMfpIMfpQMfpI9AT0AddM0PpIMfpQMfQE9AQx0SrwA4IA3xEBs/L0JW6SNQSRMeJSgIBA9A5voY4hEChfCMjPhYj6UoIQWkXUNM8Ljss/ggDfDM8L/8mAQPsA4fpI0cjPkMXaNlYayz8Yyz8m10kgqTgC8kWrAoEoAUDbIz5DF2jZWFM4Syz8h10kgqTgC8kWrAiDBQfKFzwsHzswSzPpUzskAJMnIz4WIEvpScc8LbszJgED7AABeIMFB8oXPCwcWzhTMEsz6VMzJyM+FiBT6UoIQ3PmTws8LjhPMEvpSAfoCyYBA+wAAHyBTbwBi1MS42LjCMcF8vSAADyLUxLjYuMYg'); + static CodeCell = c.Cell.fromBase64('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'); static Errors = { 'Common_Error.CrossChainAddressOutOfRange': 5, @@ -2556,13 +2704,18 @@ export class Router implements c.Contract { 'Router_Error.DestChainNotEnabled': 57100, 'Withdrawable_Error.HitReserve': 57101, 'Router_Error.SourceChainNotEnabled': 57101, - 'Router_Error.SenderIsNotOffRamp': 57102, 'Withdrawable_Error.InvalidRequest': 57102, + 'Router_Error.SenderIsNotOffRamp': 57102, 'Router_Error.OffRampNotSetForSelector': 57103, 'Router_Error.OffRampAddressMismatch': 57104, 'Router_Error.SubjectCursed': 57105, 'Router_Error.NotOnRamp': 57106, + 'Router_Error.MissingTokenAmounts': 57107, + 'Router_Error.NoMultiTokenTransfers': 57108, 'Router_Error.InsufficientFee': 57109, + 'Router_Error.TokenTransferNotThroughNotification': 57110, + 'Router_Error.JettonTransferNotificationWithoutForwardPayload': 57111, + 'Router_Error.TokenAmountMismatch': 57112, } readonly address: c.Address @@ -2708,12 +2861,30 @@ export class Router implements c.Contract { return Router_MessageRejected.toCell(Router_MessageRejected.create(body)); } + static createCellOfRouterLockOrBurn(body: { + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address + }) { + return Router_LockOrBurn.toCell(Router_LockOrBurn.create(body)); + } + static createCellOfRouterRMNOwnableMessage(body: { content: RemainingBitsAndRefs }) { return Router_RMNOwnableMessage.toCell(Router_RMNOwnableMessage.create(body)); } + static createCellOfCommonJettonTransferNotification(body: { + queryId: uint64 + amount: coins + sender: c.Address + forwardPayload: c.Cell | null + }) { + return Common_JettonTransferNotification.toCell(Common_JettonTransferNotification.create(body)); + } + async sendDeploy(provider: ContractProvider, via: Sender, msgValue: coins, extraOptions?: ExtraSendOptions) { return provider.internal(via, { value: msgValue, @@ -2882,6 +3053,19 @@ export class Router implements c.Contract { }); } + async sendRouterLockOrBurn(provider: ContractProvider, via: Sender, msgValue: coins, body: { + tokenPool: c.Address + tokenAmount: TokenAmount + destChainSelector: uint64 + executorAddress: c.Address + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: Router_LockOrBurn.toCell(Router_LockOrBurn.create(body)), + ...extraOptions + }); + } + async sendRouterRMNOwnableMessage(provider: ContractProvider, via: Sender, msgValue: coins, body: { content: RemainingBitsAndRefs }, extraOptions?: ExtraSendOptions) { @@ -2892,6 +3076,19 @@ export class Router implements c.Contract { }); } + async sendCommonJettonTransferNotification(provider: ContractProvider, via: Sender, msgValue: coins, body: { + queryId: uint64 + amount: coins + sender: c.Address + forwardPayload: c.Cell | null + }, extraOptions?: ExtraSendOptions) { + return provider.internal(via, { + value: msgValue, + body: Common_JettonTransferNotification.toCell(Common_JettonTransferNotification.create(body)), + ...extraOptions + }); + } + async getVerifyNotCursed(provider: ContractProvider, subject: uint128): Promise { const r = StackReader.fromGetMethod(1, await provider.get('verifyNotCursed', [ { type: 'int', value: subject },