Skip to content

Commit ef76665

Browse files
wip
1 parent dfd58a9 commit ef76665

15 files changed

Lines changed: 164 additions & 25 deletions

File tree

contracts/contracts/ccip/ccipsend_executor/contract.tolk

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import "@stdlib/gas-payments"
2+
13
import "types"
24
import "messages"
35
import "storage"
@@ -7,10 +9,8 @@ import "../onramp/messages"
79
import "../../lib/utils"
810
import "../router/messages"
911
import "../fee_quoter/types"
10-
import "@stdlib/gas-payments"
11-
import "../common/types"
1212
import "../token_registry/messages"
13-
import "../pools/messages"
13+
import "../lib/token_pool/messages"
1414

1515
tolk 1.4.1
1616

contracts/contracts/ccip/ccipsend_executor/messages.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import "types"
22
import "../onramp/messages"
33
import "../fee_quoter/messages"
44
import "../token_registry/messages"
5-
import "../pools/messages"
5+
import "../lib/token_pool/messages"
66

77
type CCIPSendExecutor_InMessage =
88
| CCIPSendExecutor_Execute
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import "../../common/types"
2+
3+
type TokenPool_InMessage =
4+
| TokenPool_LockOrBurn
5+
| TokenPool_ReleaseOrMint
6+
;
7+
8+
/// Incoming messages
9+
10+
struct (0x7dd8f942) TokenPool_LockOrBurn {
11+
tokenAmount: TokenAmount, // TODO LockOrBurn doesn't need to know the token address
12+
notify: address,
13+
}
14+
15+
struct (0x351f77e3) TokenPool_ReleaseOrMint {
16+
amount: coins,
17+
notify: address,
18+
}
19+
20+
/// Outgoing messages
21+
22+
struct (0x4c700579) TokenPool_NotifySuccessfulLockOrBurn {}
23+
24+
struct (0xd001f083) TokenPool_NotifySuccessfulReleaseOrMint {}

contracts/contracts/ccip/offramp/contract.tolk

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,8 @@ fun onExecuteValidated(st: Storage, msg: OffRamp_ExecuteValidated, sender: addre
800800

801801
val executorAddress: AutoDeployAddress = getReceiverExecutorDeployAddress(st, execId);
802802

803+
val tokenAdminRegistry = getTokenAdminRegistry(st, message.tokenAmounts);
804+
803805
// Check if this is the first time the exec flow is running given that in that case we need to
804806
// initialize the executor
805807
if (msg.executionState == ExecutionState.Untouched) {
@@ -815,6 +817,7 @@ fun onExecuteValidated(st: Storage, msg: OffRamp_ExecuteValidated, sender: addre
815817
message: message.toCell(),
816818
root: sender,
817819
execId,
820+
tokenAdminRegistry,
818821
}.toCell(),
819822
}
820823
},
@@ -838,6 +841,32 @@ fun onExecuteValidated(st: Storage, msg: OffRamp_ExecuteValidated, sender: addre
838841
initExecMsg.send(SEND_MODE_PAY_FEES_SEPARATELY);
839842
}
840843

844+
fun getTokenAdminRegistry(st: Storage, tokenAmounts: SnakedCell<Any2TVMTokenTransfer>?): Cell<address>? {
845+
if (tokenAmounts == null) {
846+
return null;
847+
}
848+
val tokenTransfer = tokenAmounts.getTokenTransfer(Error.UnsupportedNumberOfTokens);
849+
val registryAddress = getTokenRegistryDeployAddress(st, tokenTransfer.token).calculateAddress();
850+
return registryAddress.toCell();
851+
}
852+
853+
// TODO duplicated from Router
854+
/*
855+
Derives the token registry contract address from the token address
856+
*/
857+
fun getTokenRegistryDeployAddress(st: Storage, tokenAddress: address): AutoDeployAddress {
858+
return {
859+
stateInit: {
860+
code: st.deployables.load().deployer,
861+
data: NamespacedDeployableData({
862+
namespace: DeployableNamespace_TokenRegistry,
863+
owner: contract.getAddress(),
864+
id: tokenAddress,
865+
}),
866+
}
867+
}
868+
}
869+
841870
/*
842871
Retrieves the Router address associated with a given sourceChainSelector.
843872
Throws an error if the source chain is not enabled in the stored configs.

contracts/contracts/ccip/offramp/errors.tolk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ enum Error {
2020
OnRampAddressMismatch
2121
EmptyCommitReport
2222
MerkleRootCannotBeZero
23+
UnsupportedNumberOfTokens
2324
}

contracts/contracts/ccip/offramp/types.tolk

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ struct Any2TVMRampMessage {
6666
// maybe mark these amounts as slice remaining then parse them by hand to avoid requiring this to be a map<> at send time?
6767
}
6868

69+
// Helper to extract the single token transfer from the message, and assert that there is only one token transfer
70+
fun SnakedCell<Any2TVMTokenTransfer>.getTokenTransfer(self, error: int): Any2TVMTokenTransfer {
71+
var it = self.iter();
72+
assert (!it.empty(), error);
73+
val token = it.next();
74+
assert (it.empty(), error);
75+
return token
76+
}
77+
6978
fun Any2TVMRampMessage.generateMessageId(self, metadataHash: uint256): uint256 {
7079
// Loosely based on https://github.com/smartcontractkit/chainlink-ccip/blob/2d2ce6abb3f7626dbd3008e4232da4faa839feff/chains/evm/contracts/libraries/Internal.sol#L113-L134
7180
return beginCell()
@@ -99,10 +108,10 @@ struct MerkleRoot {
99108

100109
struct Any2TVMTokenTransfer {
101110
sourcePoolAddress: Cell<CrossChainAddress>;
102-
destPoolAddress: address;
103-
destGasAmount: uint32;
111+
token: address;
112+
destGasAmount: uint32; // TBD: This field was removed in CCIP 2.0 and it's purpose/utility in TON is unclear.
104113
extraData: cell;
105-
amount: uint256;
114+
amount: coins; // TBD: Does this change break any message ID we set in the past? It would but we didn't support token transfers then
106115
}
107116

108117
struct OffRamp_Deployables {

contracts/contracts/ccip/pools/messages.tolk

Lines changed: 0 additions & 1 deletion
This file was deleted.

contracts/contracts/ccip/receive_executor/contract.tolk

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import "types"
66
import "errors"
77
import "../offramp/messages"
88
import "../../lib/utils"
9+
import "../lib/token_pool/messages"
10+
import "../token_registry/messages"
911

1012
tolk 1.4.1
1113

12-
const CONTRACT_VERSION = "1.6.2".literalSlice();
14+
const CONTRACT_VERSION = "1.7.0".literalSlice();
1315

1416
contract ReceiveExecutor {
1517
author: "SmartContract Chainlink Limited SEZC"
16-
version: "1.6.2"
18+
version: "1.7.0"
1719
description: "link.chain.ton.ccip.ReceiveExecutor"
1820

1921
storage: ReceiveExecutor_Storage
@@ -37,6 +39,16 @@ fun onInternalMessage(in: InMessage) {
3739
onCCIPReceiveConfirm(mutate st, msg);
3840
st.store();
3941
}
42+
// Sender must be the TokenAdminRegistry
43+
TokenRegistry_ReturnTokenInfo => {
44+
var st = ReceiveExecutor_Storage.load();
45+
assert(st.tokenAdminRegistry != null, Error.Unauthorized);
46+
val tokenAdminRegistry = st.tokenAdminRegistry.load();
47+
assert(in.senderAddress == tokenAdminRegistry, Error.Unauthorized);
48+
assert(st.state == ReceiveExecutor_MessageState.TokenAdminRegistryQuery, Error.TokenAdminRegistryUnexpectedResponse);
49+
onTokenAdminRegistryResponse(mutate st, msg);
50+
st.store();
51+
}
4052
// Sender must be the owner (OffRamp)
4153
ReceiveExecutor_Bounced => {
4254
var st = ReceiveExecutor_Storage.load();
@@ -52,11 +64,49 @@ fun onInternalMessage(in: InMessage) {
5264
}
5365

5466
fun onInitExecute(mutate st: ReceiveExecutor_Storage, msg: ReceiveExecutor_InitExecute) {
55-
st.lastExecutionTimestamp = blockchain.now();
67+
if (st.tokenAdminRegistry != null) {
68+
return queryTokenAdminRegistry(mutate st, st.tokenAdminRegistry!.load());
69+
}
5670
execute(mutate st, msg.gasOverride);
5771
}
5872

73+
fun queryTokenAdminRegistry(mutate st: ReceiveExecutor_Storage, tokenAdminRegistry: address) {
74+
st.state = ReceiveExecutor_MessageState.TokenAdminRegistryQuery;
75+
createMessage(
76+
{
77+
bounce: true,
78+
value: 0,
79+
dest: tokenAdminRegistry,
80+
body: TokenRegistry_GetTokenInfo {
81+
}
82+
}
83+
).send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);
84+
}
85+
86+
fun onTokenAdminRegistryResponse(mutate st: ReceiveExecutor_Storage, msg: TokenRegistry_ReturnTokenInfo) {
87+
val rampMsg = lazy st.message.load();
88+
assert (rampMsg.tokenAmounts != null) throw Error.NoTokenAmountsInMessage;
89+
val token = rampMsg.tokenAmounts.getTokenTransfer(Error.UnsupportedNumberOfTokens);
90+
releaseOrMint(mutate st, token);
91+
}
92+
93+
fun releaseOrMint(mutate st: ReceiveExecutor_Storage, tokenAmount: Any2TVMTokenTransfer) {
94+
st.state = ReceiveExecutor_MessageState.TokenTransfer;
95+
createMessage(
96+
{
97+
bounce: true,
98+
value: 0, // TBD should we be using tokenAmoun.destGasAmount instead? It's not clear what this field is for in TON, and it was removed in CCIP 2.0
99+
dest: tokenAmount.token,
100+
body: TokenPool_ReleaseOrMint {
101+
amount: tokenAmount.amount,
102+
notify: contract.getAddress(),
103+
}
104+
}
105+
).send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);
106+
}
107+
59108
fun execute(mutate st: ReceiveExecutor_Storage, gasOverride: coins?) {
109+
st.lastExecutionTimestamp = blockchain.now();
60110
st.state = ReceiveExecutor_MessageState.Execute;
61111
createMessage(
62112
{

contracts/contracts/ccip/receive_executor/errors.tolk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ enum Error {
66
UpdatingStateOfNonExecutedMessage
77
NotificationFromInvalidReceiver
88
Unauthorized
9+
UnsupportedNumberOfTokens
10+
NoTokenAmountsInMessage
11+
TokenAdminRegistryUnexpectedResponse
912
}

contracts/contracts/ccip/receive_executor/messages.tolk

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// SPDX-License-Identifier: BUSL-1.1
22
import "../offramp/messages"
3+
import "../token_registry/messages"
34

45
type ReceiveExecutor_InMessage =
56
| ReceiveExecutor_InitExecute
67
| ReceiveExecutor_Bounced
7-
| ReceiveExecutor_Confirm;
8+
| ReceiveExecutor_Confirm
9+
| TokenRegistry_ReturnTokenInfo
10+
;
811

912
struct (0x64cd2fd2) ReceiveExecutor_InitExecute {
1013
gasOverride: coins? = null;

0 commit comments

Comments
 (0)