@chainlink/ccip-sdk 0.91.0 → 0.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -80
- package/dist/aptos/hasher.d.ts.map +1 -1
- package/dist/aptos/hasher.js +7 -6
- package/dist/aptos/hasher.js.map +1 -1
- package/dist/aptos/index.d.ts +7 -2
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +29 -20
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.d.ts +5 -3
- package/dist/aptos/logs.d.ts.map +1 -1
- package/dist/aptos/logs.js +64 -27
- package/dist/aptos/logs.js.map +1 -1
- package/dist/aptos/token.d.ts.map +1 -1
- package/dist/aptos/token.js +2 -1
- package/dist/aptos/token.js.map +1 -1
- package/dist/aptos/types.js +6 -6
- package/dist/aptos/types.js.map +1 -1
- package/dist/chain.d.ts +36 -11
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +34 -2
- package/dist/chain.js.map +1 -1
- package/dist/commits.d.ts +2 -3
- package/dist/commits.d.ts.map +1 -1
- package/dist/commits.js +19 -8
- package/dist/commits.js.map +1 -1
- package/dist/errors/CCIPError.d.ts +48 -0
- package/dist/errors/CCIPError.d.ts.map +1 -0
- package/dist/errors/CCIPError.js +65 -0
- package/dist/errors/CCIPError.js.map +1 -0
- package/dist/errors/codes.d.ts +120 -0
- package/dist/errors/codes.d.ts.map +1 -0
- package/dist/errors/codes.js +156 -0
- package/dist/errors/codes.js.map +1 -0
- package/dist/errors/index.d.ts +26 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +51 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/recovery.d.ts +6 -0
- package/dist/errors/recovery.d.ts.map +1 -0
- package/dist/errors/recovery.js +118 -0
- package/dist/errors/recovery.js.map +1 -0
- package/dist/errors/specialized.d.ts +637 -0
- package/dist/errors/specialized.d.ts.map +1 -0
- package/dist/errors/specialized.js +1298 -0
- package/dist/errors/specialized.js.map +1 -0
- package/dist/errors/utils.d.ts +11 -0
- package/dist/errors/utils.d.ts.map +1 -0
- package/dist/errors/utils.js +61 -0
- package/dist/errors/utils.js.map +1 -0
- package/dist/evm/abi/CommitStore_1_5.js +1 -1
- package/dist/evm/abi/LockReleaseTokenPool_1_5.js +1 -1
- package/dist/evm/abi/OffRamp_1_5.js +1 -1
- package/dist/evm/abi/OnRamp_1_5.js +1 -1
- package/dist/evm/abi/PriceRegistry_1_2.d.ts +443 -0
- package/dist/evm/abi/PriceRegistry_1_2.d.ts.map +1 -0
- package/dist/evm/abi/PriceRegistry_1_2.js +439 -0
- package/dist/evm/abi/PriceRegistry_1_2.js.map +1 -0
- package/dist/evm/const.d.ts +1 -0
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +2 -0
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/hasher.d.ts.map +1 -1
- package/dist/evm/hasher.js +7 -6
- package/dist/evm/hasher.js.map +1 -1
- package/dist/evm/index.d.ts +9 -13
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +85 -68
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/logs.d.ts.map +1 -1
- package/dist/evm/logs.js +47 -16
- package/dist/evm/logs.js.map +1 -1
- package/dist/evm/messages.d.ts +7 -6
- package/dist/evm/messages.d.ts.map +1 -1
- package/dist/evm/offchain.js +1 -1
- package/dist/evm/offchain.js.map +1 -1
- package/dist/evm/types.d.ts +10 -0
- package/dist/evm/types.d.ts.map +1 -0
- package/dist/evm/types.js +2 -0
- package/dist/evm/types.js.map +1 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +9 -5
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +4 -3
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +3 -2
- package/dist/gas.js.map +1 -1
- package/dist/hasher/hasher.d.ts.map +1 -1
- package/dist/hasher/hasher.js +2 -1
- package/dist/hasher/hasher.js.map +1 -1
- package/dist/hasher/merklemulti.d.ts.map +1 -1
- package/dist/hasher/merklemulti.js +9 -8
- package/dist/hasher/merklemulti.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +5 -8
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +1 -1
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +37 -43
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +22 -0
- package/dist/selectors.js.map +1 -1
- package/dist/solana/cleanup.d.ts +2 -2
- package/dist/solana/cleanup.d.ts.map +1 -1
- package/dist/solana/cleanup.js +2 -3
- package/dist/solana/cleanup.js.map +1 -1
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +12 -12
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/hasher.d.ts.map +1 -1
- package/dist/solana/hasher.js +6 -5
- package/dist/solana/hasher.js.map +1 -1
- package/dist/solana/index.d.ts +30 -13
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +96 -143
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/logs.d.ts +15 -0
- package/dist/solana/logs.d.ts.map +1 -0
- package/dist/solana/logs.js +106 -0
- package/dist/solana/logs.js.map +1 -0
- package/dist/solana/offchain.d.ts.map +1 -1
- package/dist/solana/offchain.js +6 -5
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/patchBorsh.d.ts.map +1 -1
- package/dist/solana/patchBorsh.js +3 -2
- package/dist/solana/patchBorsh.js.map +1 -1
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +8 -7
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/utils.d.ts +7 -8
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +23 -11
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/discovery.d.ts +18 -0
- package/dist/sui/discovery.d.ts.map +1 -0
- package/dist/sui/discovery.js +116 -0
- package/dist/sui/discovery.js.map +1 -0
- package/dist/sui/events.d.ts +36 -0
- package/dist/sui/events.d.ts.map +1 -0
- package/dist/sui/events.js +179 -0
- package/dist/sui/events.js.map +1 -0
- package/dist/sui/hasher.d.ts.map +1 -1
- package/dist/sui/hasher.js +6 -5
- package/dist/sui/hasher.js.map +1 -1
- package/dist/sui/index.d.ts +69 -41
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +402 -65
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/manuallyExec/encoder.d.ts +8 -0
- package/dist/sui/manuallyExec/encoder.d.ts.map +1 -0
- package/dist/sui/manuallyExec/encoder.js +76 -0
- package/dist/sui/manuallyExec/encoder.js.map +1 -0
- package/dist/sui/manuallyExec/index.d.ts +37 -0
- package/dist/sui/manuallyExec/index.d.ts.map +1 -0
- package/dist/sui/manuallyExec/index.js +81 -0
- package/dist/sui/manuallyExec/index.js.map +1 -0
- package/dist/sui/objects.d.ts +46 -0
- package/dist/sui/objects.d.ts.map +1 -0
- package/dist/sui/objects.js +259 -0
- package/dist/sui/objects.js.map +1 -0
- package/dist/ton/bindings/offramp.d.ts +48 -0
- package/dist/ton/bindings/offramp.d.ts.map +1 -0
- package/dist/ton/bindings/offramp.js +63 -0
- package/dist/ton/bindings/offramp.js.map +1 -0
- package/dist/ton/bindings/onramp.d.ts +40 -0
- package/dist/ton/bindings/onramp.d.ts.map +1 -0
- package/dist/ton/bindings/onramp.js +51 -0
- package/dist/ton/bindings/onramp.js.map +1 -0
- package/dist/ton/bindings/router.d.ts +47 -0
- package/dist/ton/bindings/router.d.ts.map +1 -0
- package/dist/ton/bindings/router.js +51 -0
- package/dist/ton/bindings/router.js.map +1 -0
- package/dist/ton/exec.d.ts +18 -0
- package/dist/ton/exec.d.ts.map +1 -0
- package/dist/ton/exec.js +28 -0
- package/dist/ton/exec.js.map +1 -0
- package/dist/ton/hasher.d.ts +27 -0
- package/dist/ton/hasher.d.ts.map +1 -0
- package/dist/ton/hasher.js +134 -0
- package/dist/ton/hasher.js.map +1 -0
- package/dist/ton/index.d.ts +247 -0
- package/dist/ton/index.d.ts.map +1 -0
- package/dist/ton/index.js +781 -0
- package/dist/ton/index.js.map +1 -0
- package/dist/ton/logs.d.ts +26 -0
- package/dist/ton/logs.d.ts.map +1 -0
- package/dist/ton/logs.js +126 -0
- package/dist/ton/logs.js.map +1 -0
- package/dist/ton/types.d.ts +37 -0
- package/dist/ton/types.d.ts.map +1 -0
- package/dist/ton/types.js +92 -0
- package/dist/ton/types.js.map +1 -0
- package/dist/ton/utils.d.ts +67 -0
- package/dist/ton/utils.d.ts.map +1 -0
- package/dist/ton/utils.js +425 -0
- package/dist/ton/utils.js.map +1 -0
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +52 -17
- package/dist/utils.js.map +1 -1
- package/package.json +12 -10
- package/src/aptos/hasher.ts +10 -6
- package/src/aptos/index.ts +50 -31
- package/src/aptos/logs.ts +85 -29
- package/src/aptos/token.ts +5 -1
- package/src/aptos/types.ts +6 -6
- package/src/chain.ts +83 -12
- package/src/commits.ts +23 -11
- package/src/errors/CCIPError.ts +86 -0
- package/src/errors/codes.ts +179 -0
- package/src/errors/index.ts +175 -0
- package/src/errors/recovery.ts +170 -0
- package/src/errors/specialized.ts +1655 -0
- package/src/errors/utils.ts +73 -0
- package/src/evm/abi/CommitStore_1_5.ts +1 -1
- package/src/evm/abi/LockReleaseTokenPool_1_5.ts +1 -1
- package/src/evm/abi/OffRamp_1_5.ts +1 -1
- package/src/evm/abi/OnRamp_1_5.ts +1 -1
- package/src/evm/abi/PriceRegistry_1_2.ts +438 -0
- package/src/evm/const.ts +2 -0
- package/src/evm/hasher.ts +7 -6
- package/src/evm/index.ts +104 -86
- package/src/evm/logs.ts +64 -16
- package/src/evm/messages.ts +14 -14
- package/src/evm/offchain.ts +1 -1
- package/src/evm/types.ts +11 -0
- package/src/execution.ts +13 -9
- package/src/extra-args.ts +4 -3
- package/src/gas.ts +10 -3
- package/src/hasher/hasher.ts +2 -1
- package/src/hasher/merklemulti.ts +18 -8
- package/src/index.ts +14 -2
- package/src/offchain.ts +10 -14
- package/src/requests.ts +51 -53
- package/src/selectors.ts +23 -0
- package/src/solana/cleanup.ts +2 -4
- package/src/solana/exec.ts +13 -13
- package/src/solana/hasher.ts +9 -5
- package/src/solana/index.ts +126 -200
- package/src/solana/logs.ts +155 -0
- package/src/solana/offchain.ts +10 -7
- package/src/solana/patchBorsh.ts +3 -2
- package/src/solana/send.ts +14 -7
- package/src/solana/utils.ts +31 -17
- package/src/sui/discovery.ts +163 -0
- package/src/sui/events.ts +328 -0
- package/src/sui/hasher.ts +6 -5
- package/src/sui/index.ts +528 -80
- package/src/sui/manuallyExec/encoder.ts +88 -0
- package/src/sui/manuallyExec/index.ts +137 -0
- package/src/sui/objects.ts +358 -0
- package/src/ton/bindings/offramp.ts +96 -0
- package/src/ton/bindings/onramp.ts +72 -0
- package/src/ton/bindings/router.ts +65 -0
- package/src/ton/exec.ts +44 -0
- package/src/ton/hasher.ts +184 -0
- package/src/ton/index.ts +989 -0
- package/src/ton/logs.ts +157 -0
- package/src/ton/types.ts +143 -0
- package/src/ton/utils.ts +514 -0
- package/src/types.ts +6 -2
- package/src/utils.ts +58 -23
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { bcs } from '@mysten/sui/bcs'
|
|
2
|
+
import type { BytesLike } from 'ethers'
|
|
3
|
+
|
|
4
|
+
import { CCIPMessageInvalidError } from '../../errors/specialized.ts'
|
|
5
|
+
import { decodeExtraArgs } from '../../extra-args.ts'
|
|
6
|
+
import type { CCIPMessage, CCIPVersion, ExecutionReport } from '../../types.ts'
|
|
7
|
+
import { bytesToBuffer, getAddressBytes, getDataBytes, networkInfo } from '../../utils.ts'
|
|
8
|
+
|
|
9
|
+
const Any2SuiTokenTransferBCS = bcs.struct('Any2SuiTokenTransfer', {
|
|
10
|
+
source_pool_address: bcs.vector(bcs.u8()),
|
|
11
|
+
dest_token_address: bcs.Address,
|
|
12
|
+
dest_gas_amount: bcs.u32(),
|
|
13
|
+
extra_data: bcs.vector(bcs.u8()),
|
|
14
|
+
amount: bcs.u256(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const ExecutionReportBCS = bcs.struct('ExecutionReport', {
|
|
18
|
+
source_chain_selector: bcs.u64(),
|
|
19
|
+
message_id: bcs.fixedArray(32, bcs.u8()),
|
|
20
|
+
header_source_chain_selector: bcs.u64(),
|
|
21
|
+
dest_chain_selector: bcs.u64(),
|
|
22
|
+
sequence_number: bcs.u64(),
|
|
23
|
+
nonce: bcs.u64(),
|
|
24
|
+
sender: bcs.vector(bcs.u8()),
|
|
25
|
+
data: bcs.vector(bcs.u8()),
|
|
26
|
+
receiver: bcs.Address,
|
|
27
|
+
gas_limit: bcs.u256(),
|
|
28
|
+
token_receiver: bcs.Address,
|
|
29
|
+
token_amounts: bcs.vector(Any2SuiTokenTransferBCS),
|
|
30
|
+
offchain_token_data: bcs.vector(bcs.vector(bcs.u8())),
|
|
31
|
+
proofs: bcs.vector(bcs.fixedArray(32, bcs.u8())),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Serializes an execution report for Sui manual execution.
|
|
36
|
+
* @param executionReport - The execution report to serialize.
|
|
37
|
+
* @returns Serialized execution report as Uint8Array.
|
|
38
|
+
*/
|
|
39
|
+
export function serializeExecutionReport(
|
|
40
|
+
executionReport: ExecutionReport<CCIPMessage<typeof CCIPVersion.V1_6>>,
|
|
41
|
+
): Uint8Array {
|
|
42
|
+
const { message, offchainTokenData, proofs } = executionReport
|
|
43
|
+
|
|
44
|
+
if (!message) {
|
|
45
|
+
throw new CCIPMessageInvalidError('Message is undefined in execution report')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let gasLimit, tokenReceiver
|
|
49
|
+
if ('receiverObjectIds' in message) {
|
|
50
|
+
;({ gasLimit, tokenReceiver } = message)
|
|
51
|
+
} else {
|
|
52
|
+
const decodedExtraArgs = decodeExtraArgs(
|
|
53
|
+
message.extraArgs,
|
|
54
|
+
networkInfo(message.sourceChainSelector).family,
|
|
55
|
+
)
|
|
56
|
+
if (decodedExtraArgs?._tag !== 'SuiExtraArgsV1')
|
|
57
|
+
throw new CCIPMessageInvalidError('Expected Sui extra args')
|
|
58
|
+
;({ gasLimit, tokenReceiver } = decodedExtraArgs)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const reportData = {
|
|
62
|
+
source_chain_selector: message.sourceChainSelector,
|
|
63
|
+
message_id: Array.from(bytesToBuffer(message.messageId)),
|
|
64
|
+
header_source_chain_selector: message.sourceChainSelector,
|
|
65
|
+
dest_chain_selector: message.destChainSelector,
|
|
66
|
+
sequence_number: message.sequenceNumber,
|
|
67
|
+
nonce: message.nonce,
|
|
68
|
+
sender: Array.from(bytesToBuffer(message.sender)),
|
|
69
|
+
data: Array.from(bytesToBuffer(message.data)),
|
|
70
|
+
receiver: message.receiver,
|
|
71
|
+
gas_limit: gasLimit,
|
|
72
|
+
token_receiver: tokenReceiver,
|
|
73
|
+
token_amounts: message.tokenAmounts.map((token) => ({
|
|
74
|
+
source_pool_address: Array.from(getAddressBytes(token.sourcePoolAddress)),
|
|
75
|
+
dest_token_address: token.destTokenAddress,
|
|
76
|
+
dest_gas_amount: Number(token.destGasAmount || 0n), // Use actual destGasAmount from token data
|
|
77
|
+
extra_data: Array.from(getDataBytes(token.extraData)),
|
|
78
|
+
amount: BigInt(token.amount),
|
|
79
|
+
})),
|
|
80
|
+
// TODO: encode as per CCTP/LBTC TPs, when available
|
|
81
|
+
offchain_token_data: offchainTokenData.map(() => Buffer.from([])),
|
|
82
|
+
proofs: proofs.map((proof: BytesLike) => {
|
|
83
|
+
return Array.from(getDataBytes(proof))
|
|
84
|
+
}),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return ExecutionReportBCS.serialize(reportData).toBytes()
|
|
88
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
2
|
+
|
|
3
|
+
import { serializeExecutionReport } from './encoder.ts'
|
|
4
|
+
import { CCIPMessageInvalidError } from '../../errors/specialized.ts'
|
|
5
|
+
import { decodeExtraArgs } from '../../extra-args.ts'
|
|
6
|
+
import type { ExecutionReport } from '../../types.ts'
|
|
7
|
+
import { networkInfo } from '../../utils.ts'
|
|
8
|
+
import type { CCIPMessage_V1_6_Sui } from '../types.ts'
|
|
9
|
+
|
|
10
|
+
/** Configuration for manually executing a Sui receiver module. */
|
|
11
|
+
export type ManuallyExecuteSuiReceiverConfig = {
|
|
12
|
+
moduleName: string
|
|
13
|
+
packageId: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Configuration for a token pool in manual execution. */
|
|
17
|
+
export type TokenConfig = {
|
|
18
|
+
tokenPoolPackageId: string
|
|
19
|
+
tokenPoolModule: string
|
|
20
|
+
tokenType: string
|
|
21
|
+
administrator: string
|
|
22
|
+
pendingAdministrator: string
|
|
23
|
+
tokenPoolTypeProof: string
|
|
24
|
+
lockOrBurnParams: string[]
|
|
25
|
+
releaseOrMintParams: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Input parameters for building a Sui manual execution transaction. */
|
|
29
|
+
export type SuiManuallyExecuteInput = {
|
|
30
|
+
offrampAddress: string
|
|
31
|
+
executionReport: ExecutionReport<CCIPMessage_V1_6_Sui>
|
|
32
|
+
ccipAddress: string
|
|
33
|
+
ccipObjectRef: string
|
|
34
|
+
offrampStateObject: string
|
|
35
|
+
receiverConfig: ManuallyExecuteSuiReceiverConfig
|
|
36
|
+
tokenConfigs?: TokenConfig[]
|
|
37
|
+
overrideReceiverObjectIds?: string[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Builds a Sui Programmable Transaction Block for manual CCIP message execution.
|
|
42
|
+
* @param params - Input parameters for building the manual execution transaction.
|
|
43
|
+
* @returns A Transaction object ready to be signed and executed.
|
|
44
|
+
*/
|
|
45
|
+
export function buildManualExecutionPTB({
|
|
46
|
+
offrampAddress,
|
|
47
|
+
executionReport,
|
|
48
|
+
ccipAddress,
|
|
49
|
+
ccipObjectRef,
|
|
50
|
+
offrampStateObject,
|
|
51
|
+
receiverConfig,
|
|
52
|
+
tokenConfigs,
|
|
53
|
+
overrideReceiverObjectIds,
|
|
54
|
+
}: SuiManuallyExecuteInput): Transaction {
|
|
55
|
+
const reportBytes = serializeExecutionReport(executionReport)
|
|
56
|
+
|
|
57
|
+
// Create transaction
|
|
58
|
+
const tx = new Transaction()
|
|
59
|
+
|
|
60
|
+
// Step 1: Call manually_init_execute to prepare the execution
|
|
61
|
+
const receiverParamsArg = tx.moveCall({
|
|
62
|
+
target: `${offrampAddress}::offramp::manually_init_execute`,
|
|
63
|
+
arguments: [
|
|
64
|
+
tx.object(ccipObjectRef),
|
|
65
|
+
tx.object(offrampStateObject),
|
|
66
|
+
tx.object('0x6'), // Clock object
|
|
67
|
+
tx.pure.vector('u8', Array.from(reportBytes)),
|
|
68
|
+
],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Get the message from the from the report using the offramp helper
|
|
72
|
+
const messageArg = tx.moveCall({
|
|
73
|
+
target: `${ccipAddress}::offramp_state_helper::extract_any2sui_message`,
|
|
74
|
+
arguments: [receiverParamsArg],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Process token pool transfers
|
|
78
|
+
if (tokenConfigs && tokenConfigs.length > 0) {
|
|
79
|
+
if (executionReport.message.tokenAmounts.length !== tokenConfigs.length) {
|
|
80
|
+
throw new CCIPMessageInvalidError('Token amounts length does not match token configs length')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Process each token transfer
|
|
84
|
+
for (let i = 0; i < tokenConfigs.length; i++) {
|
|
85
|
+
const tokenConfig = tokenConfigs[i]
|
|
86
|
+
|
|
87
|
+
tx.moveCall({
|
|
88
|
+
target: `${tokenConfig.tokenPoolPackageId}::${tokenConfig.tokenPoolModule}::release_or_mint`,
|
|
89
|
+
typeArguments: [tokenConfig.tokenType],
|
|
90
|
+
arguments: [
|
|
91
|
+
tx.object(ccipObjectRef), // CCIPObjectRef
|
|
92
|
+
receiverParamsArg, // ReceiverParams (mutable)
|
|
93
|
+
...tokenConfig.releaseOrMintParams.map((param) => tx.object(param)), // Pool-specific objects (clock, deny_list, token_state, state, etc.)
|
|
94
|
+
],
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Decode extraArgs to get receiverObjectIds
|
|
100
|
+
const decodedExtraArgs = decodeExtraArgs(
|
|
101
|
+
executionReport.message.extraArgs,
|
|
102
|
+
networkInfo(executionReport.message.destChainSelector).family,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if (!decodedExtraArgs || decodedExtraArgs._tag !== 'SuiExtraArgsV1') {
|
|
106
|
+
throw new CCIPMessageInvalidError('Expected Sui extra args')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (decodedExtraArgs.receiverObjectIds.length === 0) {
|
|
110
|
+
throw new CCIPMessageInvalidError('No receiverObjectIds provided in SUIExtraArgsV1')
|
|
111
|
+
}
|
|
112
|
+
// Call the receiver contract
|
|
113
|
+
tx.moveCall({
|
|
114
|
+
target: `${receiverConfig.packageId}::${receiverConfig.moduleName}::ccip_receive`,
|
|
115
|
+
arguments: [
|
|
116
|
+
tx.pure.vector('u8', Buffer.from(executionReport.message.messageId.slice(2), 'hex')),
|
|
117
|
+
tx.object(ccipObjectRef),
|
|
118
|
+
messageArg,
|
|
119
|
+
// if overrideReceiverObjectIds is provided, use them; otherwise, use the ones from decodedExtraArgs (original message)
|
|
120
|
+
...(overrideReceiverObjectIds && overrideReceiverObjectIds.length > 0
|
|
121
|
+
? overrideReceiverObjectIds.map(tx.object)
|
|
122
|
+
: decodedExtraArgs.receiverObjectIds.map(tx.object)),
|
|
123
|
+
],
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Step 2: Call finish_execute to complete the execution
|
|
127
|
+
tx.moveCall({
|
|
128
|
+
target: `${offrampAddress}::offramp::finish_execute`,
|
|
129
|
+
arguments: [
|
|
130
|
+
tx.object(ccipObjectRef),
|
|
131
|
+
tx.object(offrampStateObject),
|
|
132
|
+
receiverParamsArg, // ReceiverParams from manually_init_execute
|
|
133
|
+
],
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
return tx
|
|
137
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { bcs } from '@mysten/sui/bcs'
|
|
2
|
+
import type { SuiClient } from '@mysten/sui/client'
|
|
3
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
4
|
+
import { normalizeSuiAddress } from '@mysten/sui/utils'
|
|
5
|
+
import { blake2b } from '@noble/hashes/blake2'
|
|
6
|
+
|
|
7
|
+
import { CCIPDataFormatUnsupportedError } from '../errors/index.ts'
|
|
8
|
+
import type { CCIPMessage, CCIPVersion } from '../types.ts'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Derive a dynamic field object ID using the Sui algorithm
|
|
12
|
+
* This matches the Go implementation in chainlink-sui
|
|
13
|
+
*/
|
|
14
|
+
export function deriveObjectID(parentAddress: string, keyBytes: Uint8Array): string {
|
|
15
|
+
// Normalize parent address to 32 bytes
|
|
16
|
+
const normalizedParent = normalizeSuiAddress(parentAddress)
|
|
17
|
+
const parentBytes = bcs.Address.serialize(normalizedParent).toBytes()
|
|
18
|
+
|
|
19
|
+
// BCS serialize the key (vector<u8>)
|
|
20
|
+
const bcsKeyBytes = bcs.vector(bcs.u8()).serialize(Array.from(keyBytes)).toBytes()
|
|
21
|
+
|
|
22
|
+
// Construct TypeTag for DerivedObjectKey<vector<u8>>
|
|
23
|
+
const suiFrameworkAddress = bcs.Address.serialize('0x2').toBytes()
|
|
24
|
+
const typeTagBytes = new Uint8Array([
|
|
25
|
+
0x07, // TypeTag::Struct
|
|
26
|
+
...suiFrameworkAddress,
|
|
27
|
+
0x0e, // module length
|
|
28
|
+
...new TextEncoder().encode('derived_object'),
|
|
29
|
+
0x10, // struct name length
|
|
30
|
+
...new TextEncoder().encode('DerivedObjectKey'),
|
|
31
|
+
0x01, // type params count
|
|
32
|
+
...[0x06, 0x01], // vector<u8> TypeTag
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
// Build the hash input
|
|
36
|
+
const keyLenBytes = new Uint8Array(8)
|
|
37
|
+
new DataView(keyLenBytes.buffer).setBigUint64(0, BigInt(bcsKeyBytes.length), true)
|
|
38
|
+
|
|
39
|
+
const hashInput = new Uint8Array([
|
|
40
|
+
0xf0, // HashingIntentScope::ChildObjectId
|
|
41
|
+
...parentBytes,
|
|
42
|
+
...keyLenBytes,
|
|
43
|
+
...bcsKeyBytes,
|
|
44
|
+
...typeTagBytes,
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
// Hash with Blake2b-256
|
|
48
|
+
const hash = blake2b(hashInput, { dkLen: 32 })
|
|
49
|
+
|
|
50
|
+
// Convert to address string
|
|
51
|
+
return normalizeSuiAddress('0x' + Buffer.from(hash).toString('hex'))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the CCIPObjectRef ID for a CCIP package
|
|
56
|
+
*/
|
|
57
|
+
export async function getCcipObjectRef(client: SuiClient, ccipPackageId: string): Promise<string> {
|
|
58
|
+
// Get the pointer to find the CCIPObject ID
|
|
59
|
+
const pointerResponse = await client.getOwnedObjects({
|
|
60
|
+
owner: ccipPackageId,
|
|
61
|
+
filter: {
|
|
62
|
+
StructType: `${ccipPackageId}::state_object::CCIPObjectRefPointer`,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (pointerResponse.data.length === 0) {
|
|
67
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
68
|
+
'No CCIPObjectRefPointer found for the given packageId',
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get the pointer object to extract ccip_object_id
|
|
73
|
+
const pointerId = pointerResponse.data[0].data?.objectId
|
|
74
|
+
if (!pointerId) {
|
|
75
|
+
throw new CCIPDataFormatUnsupportedError('Pointer does not have objectId')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const pointerObject = await client.getObject({
|
|
79
|
+
id: pointerId,
|
|
80
|
+
options: { showContent: true },
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (pointerObject.data?.content?.dataType !== 'moveObject') {
|
|
84
|
+
throw new CCIPDataFormatUnsupportedError('Pointer object is not a Move object')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const ccipObjectId = (pointerObject.data.content.fields as Record<string, unknown>)[
|
|
88
|
+
'ccip_object_id'
|
|
89
|
+
] as string
|
|
90
|
+
|
|
91
|
+
if (!ccipObjectId) {
|
|
92
|
+
throw new CCIPDataFormatUnsupportedError('Could not find ccip_object_id in pointer')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Derive the CCIPObjectRef ID from the parent CCIPObject ID
|
|
96
|
+
return deriveObjectID(ccipObjectId, new TextEncoder().encode('CCIPObjectRef'))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get the OffRampState object ID for an offramp package
|
|
101
|
+
*/
|
|
102
|
+
export async function getOffRampStateObject(
|
|
103
|
+
client: SuiClient,
|
|
104
|
+
offrampPackageId: string,
|
|
105
|
+
): Promise<string> {
|
|
106
|
+
const offrampPointerResponse = await client.getOwnedObjects({
|
|
107
|
+
owner: offrampPackageId,
|
|
108
|
+
filter: {
|
|
109
|
+
StructType: `${offrampPackageId}::offramp::OffRampStatePointer`,
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
if (offrampPointerResponse.data.length === 0) {
|
|
114
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
115
|
+
'No OffRampStatePointer found for the given offramp package',
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const offrampPointerId = offrampPointerResponse.data[0].data?.objectId
|
|
120
|
+
|
|
121
|
+
if (!offrampPointerId) {
|
|
122
|
+
throw new CCIPDataFormatUnsupportedError('OffRampStatePointer does not have a valid objectId')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const offrampPointerObject = await client.getObject({
|
|
126
|
+
id: offrampPointerId,
|
|
127
|
+
options: { showContent: true },
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
if (offrampPointerObject.data?.content?.dataType !== 'moveObject') {
|
|
131
|
+
throw new CCIPDataFormatUnsupportedError('OffRamp pointer object is not a Move object')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const offrampObjectId = (offrampPointerObject.data.content.fields as Record<string, unknown>)[
|
|
135
|
+
'off_ramp_object_id'
|
|
136
|
+
] as string
|
|
137
|
+
|
|
138
|
+
if (!offrampObjectId) {
|
|
139
|
+
throw new CCIPDataFormatUnsupportedError('Could not find off_ramp_object_id in pointer')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Derive the OffRampState ID from the parent OffRamp Object ID
|
|
143
|
+
return deriveObjectID(offrampObjectId, new TextEncoder().encode('OffRampState'))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the receiver module configuration from the receiver registry.
|
|
148
|
+
* @param provider - Sui client
|
|
149
|
+
* @param ccipPackageId - CCIP package ID
|
|
150
|
+
* @param ccipObjectRef - CCIP object reference
|
|
151
|
+
* @param receiverPackageId - Receiver package ID
|
|
152
|
+
* @returns Receiver module name and package ID
|
|
153
|
+
*/
|
|
154
|
+
export async function getReceiverModule(
|
|
155
|
+
provider: SuiClient,
|
|
156
|
+
ccipPackageId: string,
|
|
157
|
+
ccipObjectRef: string,
|
|
158
|
+
receiverPackageId: string,
|
|
159
|
+
) {
|
|
160
|
+
// Call get_receiver_config from receiver_registry contract
|
|
161
|
+
const tx = new Transaction()
|
|
162
|
+
|
|
163
|
+
tx.moveCall({
|
|
164
|
+
target: `${ccipPackageId}::receiver_registry::get_receiver_config`,
|
|
165
|
+
arguments: [tx.object(ccipObjectRef), tx.pure.address(receiverPackageId)],
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const result = await provider.devInspectTransactionBlock({
|
|
169
|
+
transactionBlock: tx,
|
|
170
|
+
sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
if (result.error) {
|
|
174
|
+
throw new CCIPDataFormatUnsupportedError(`Failed to call get_receiver_config: ${result.error}`)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!result.results || result.results.length === 0) {
|
|
178
|
+
throw new CCIPDataFormatUnsupportedError('No results returned from get_receiver_config')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const returnValues = result.results[0]?.returnValues
|
|
182
|
+
|
|
183
|
+
if (!returnValues || returnValues.length === 0) {
|
|
184
|
+
throw new CCIPDataFormatUnsupportedError('No return values from get_receiver_config')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Decode the ReceiverConfig struct
|
|
188
|
+
// ReceiverConfig has two fields: module_name (String) and proof_typename (ascii::String)
|
|
189
|
+
// The struct is returned as a BCS-encoded byte array
|
|
190
|
+
const receiverConfigBytes = returnValues[0][0]
|
|
191
|
+
|
|
192
|
+
// Parse the struct:
|
|
193
|
+
// First field is module_name (String = vector<u8> with length prefix)
|
|
194
|
+
let offset = 0
|
|
195
|
+
const moduleNameLength = receiverConfigBytes[offset]
|
|
196
|
+
offset += 1
|
|
197
|
+
const moduleName = new TextDecoder().decode(
|
|
198
|
+
new Uint8Array(receiverConfigBytes.slice(offset, offset + moduleNameLength)),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
moduleName,
|
|
203
|
+
packageId: receiverPackageId,
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Fetch token configurations for the given token amounts.
|
|
209
|
+
* @param client - Sui client
|
|
210
|
+
* @param ccipPackageId - CCIP package ID
|
|
211
|
+
* @param ccipObjectRef - CCIP object reference
|
|
212
|
+
* @param tokenAmounts - Token amounts from CCIP message
|
|
213
|
+
* @returns Array of token configurations
|
|
214
|
+
*/
|
|
215
|
+
export async function fetchTokenConfigs(
|
|
216
|
+
client: SuiClient,
|
|
217
|
+
ccipPackageId: string,
|
|
218
|
+
ccipObjectRef: string,
|
|
219
|
+
tokenAmounts: CCIPMessage<typeof CCIPVersion.V1_6>['tokenAmounts'],
|
|
220
|
+
) {
|
|
221
|
+
if (tokenAmounts.length === 0) {
|
|
222
|
+
return []
|
|
223
|
+
}
|
|
224
|
+
const tokenConfigs = []
|
|
225
|
+
const tokenAddresses = [
|
|
226
|
+
...new Set(
|
|
227
|
+
tokenAmounts.map((token) => token.destTokenAddress).filter((addr) => addr && addr !== '0x0'),
|
|
228
|
+
),
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
// Fetch token config for each unique token address
|
|
232
|
+
for (const tokenAddress of tokenAddresses) {
|
|
233
|
+
const tx = new Transaction()
|
|
234
|
+
|
|
235
|
+
// Call get_token_config_struct from token_admin_registry
|
|
236
|
+
tx.moveCall({
|
|
237
|
+
target: `${ccipPackageId}::token_admin_registry::get_token_config_struct`,
|
|
238
|
+
arguments: [tx.object(ccipObjectRef), tx.pure.address(tokenAddress)],
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const result = await client.devInspectTransactionBlock({
|
|
242
|
+
transactionBlock: tx,
|
|
243
|
+
sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
if (result.error) {
|
|
247
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
248
|
+
`Failed to fetch token config for ${tokenAddress}: ${result.error}`,
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!result.results || result.results.length === 0) {
|
|
253
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
254
|
+
`No results returned from get_token_config_struct for ${tokenAddress}`,
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const returnValues = result.results[0]?.returnValues
|
|
259
|
+
|
|
260
|
+
if (!returnValues || returnValues.length === 0) {
|
|
261
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
262
|
+
`No return values from get_token_config_struct for ${tokenAddress}`,
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Parse the TokenConfig struct from BCS-encoded bytes
|
|
267
|
+
const configBytes = returnValues[0][0]
|
|
268
|
+
|
|
269
|
+
// TokenConfig structure (from token_admin_registry.go):
|
|
270
|
+
// - TokenPoolPackageId (address = 32 bytes)
|
|
271
|
+
// - TokenPoolModule (String = length + bytes)
|
|
272
|
+
// - TokenType (ascii::String = length + bytes)
|
|
273
|
+
// - Administrator (address = 32 bytes)
|
|
274
|
+
// - PendingAdministrator (address = 32 bytes)
|
|
275
|
+
// - TokenPoolTypeProof (ascii::String = length + bytes)
|
|
276
|
+
// - LockOrBurnParams (vector<address> = length + N * 32 bytes)
|
|
277
|
+
// - ReleaseOrMintParams (vector<address> = length + N * 32 bytes)
|
|
278
|
+
|
|
279
|
+
let offset = 0
|
|
280
|
+
|
|
281
|
+
// TokenPoolPackageId (32 bytes)
|
|
282
|
+
const tokenPoolPackageIdBytes = configBytes.slice(offset, offset + 32)
|
|
283
|
+
const tokenPoolPackageId = normalizeSuiAddress(
|
|
284
|
+
'0x' + Buffer.from(tokenPoolPackageIdBytes).toString('hex'),
|
|
285
|
+
)
|
|
286
|
+
offset += 32
|
|
287
|
+
|
|
288
|
+
// TokenPoolModule (String)
|
|
289
|
+
const moduleNameLength = configBytes[offset]
|
|
290
|
+
offset += 1
|
|
291
|
+
const tokenPoolModule = new TextDecoder().decode(
|
|
292
|
+
new Uint8Array(configBytes.slice(offset, offset + moduleNameLength)),
|
|
293
|
+
)
|
|
294
|
+
offset += moduleNameLength
|
|
295
|
+
|
|
296
|
+
// TokenType (ascii::String)
|
|
297
|
+
const tokenTypeLength = configBytes[offset]
|
|
298
|
+
offset += 1
|
|
299
|
+
const tokenType = new TextDecoder().decode(
|
|
300
|
+
new Uint8Array(configBytes.slice(offset, offset + tokenTypeLength)),
|
|
301
|
+
)
|
|
302
|
+
offset += tokenTypeLength
|
|
303
|
+
|
|
304
|
+
// Administrator (32 bytes)
|
|
305
|
+
const administratorBytes = configBytes.slice(offset, offset + 32)
|
|
306
|
+
const administrator = normalizeSuiAddress(
|
|
307
|
+
'0x' + Buffer.from(administratorBytes).toString('hex'),
|
|
308
|
+
)
|
|
309
|
+
offset += 32
|
|
310
|
+
|
|
311
|
+
// PendingAdministrator (32 bytes)
|
|
312
|
+
const pendingAdminBytes = configBytes.slice(offset, offset + 32)
|
|
313
|
+
const pendingAdministrator = normalizeSuiAddress(
|
|
314
|
+
'0x' + Buffer.from(pendingAdminBytes).toString('hex'),
|
|
315
|
+
)
|
|
316
|
+
offset += 32
|
|
317
|
+
|
|
318
|
+
// TokenPoolTypeProof (ascii::String)
|
|
319
|
+
const proofLength = configBytes[offset]
|
|
320
|
+
offset += 1
|
|
321
|
+
const tokenPoolTypeProof = new TextDecoder().decode(
|
|
322
|
+
new Uint8Array(configBytes.slice(offset, offset + proofLength)),
|
|
323
|
+
)
|
|
324
|
+
offset += proofLength
|
|
325
|
+
|
|
326
|
+
// LockOrBurnParams (vector<address>)
|
|
327
|
+
const lockOrBurnCount = configBytes[offset]
|
|
328
|
+
offset += 1
|
|
329
|
+
const lockOrBurnParams: string[] = []
|
|
330
|
+
for (let i = 0; i < lockOrBurnCount; i++) {
|
|
331
|
+
const addrBytes = configBytes.slice(offset, offset + 32)
|
|
332
|
+
lockOrBurnParams.push(normalizeSuiAddress('0x' + Buffer.from(addrBytes).toString('hex')))
|
|
333
|
+
offset += 32
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ReleaseOrMintParams (vector<address>)
|
|
337
|
+
const releaseOrMintCount = configBytes[offset]
|
|
338
|
+
offset += 1
|
|
339
|
+
const releaseOrMintParams: string[] = []
|
|
340
|
+
for (let i = 0; i < releaseOrMintCount; i++) {
|
|
341
|
+
const addrBytes = configBytes.slice(offset, offset + 32)
|
|
342
|
+
releaseOrMintParams.push(normalizeSuiAddress('0x' + Buffer.from(addrBytes).toString('hex')))
|
|
343
|
+
offset += 32
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
tokenConfigs.push({
|
|
347
|
+
tokenPoolPackageId,
|
|
348
|
+
tokenPoolModule,
|
|
349
|
+
tokenType,
|
|
350
|
+
administrator,
|
|
351
|
+
pendingAdministrator,
|
|
352
|
+
tokenPoolTypeProof,
|
|
353
|
+
lockOrBurnParams,
|
|
354
|
+
releaseOrMintParams,
|
|
355
|
+
})
|
|
356
|
+
}
|
|
357
|
+
return tokenConfigs
|
|
358
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// TODO: FIXME: Remove local copies and import when chainlink-ton is published as npm package
|
|
2
|
+
import type { Address, ContractProvider } from '@ton/core'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for a source chain on the TON OffRamp contract.
|
|
6
|
+
*/
|
|
7
|
+
export interface SourceChainConfig {
|
|
8
|
+
router: Address
|
|
9
|
+
isEnabled: boolean
|
|
10
|
+
minSeqNr: bigint
|
|
11
|
+
isRMNVerificationDisabled: boolean
|
|
12
|
+
onRamp: Buffer
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Dynamic configuration for the OffRamp contract.
|
|
17
|
+
* Contains addresses that can be updated without redeploying the contract.
|
|
18
|
+
*/
|
|
19
|
+
export interface DynamicConfig {
|
|
20
|
+
router: Address
|
|
21
|
+
feeQuoter: Address
|
|
22
|
+
permissionlessExecutionThresholdSeconds: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* TON OffRamp contract binding.
|
|
27
|
+
* The OffRamp receives and executes cross-chain messages on the destination (TON) chain.
|
|
28
|
+
*/
|
|
29
|
+
export class OffRamp {
|
|
30
|
+
readonly address: Address
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new OffRamp instance.
|
|
34
|
+
* @param address - The OffRamp contract address on TON.
|
|
35
|
+
*/
|
|
36
|
+
constructor(address: Address) {
|
|
37
|
+
this.address = address
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates an OffRamp instance from a contract address.
|
|
42
|
+
* @param address - The OffRamp contract address on TON.
|
|
43
|
+
* @returns A new OffRamp instance.
|
|
44
|
+
*/
|
|
45
|
+
static createFromAddress(address: Address): OffRamp {
|
|
46
|
+
return new OffRamp(address)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves the source chain configuration for a given chain selector.
|
|
51
|
+
*
|
|
52
|
+
* @param provider - TON contract provider for making RPC calls.
|
|
53
|
+
* @param sourceChainSelector - The CCIP chain selector of the source chain.
|
|
54
|
+
* @returns The source chain configuration.
|
|
55
|
+
* @throws Error with exitCode 266 if the source chain is not enabled.
|
|
56
|
+
*/
|
|
57
|
+
async getSourceChainConfig(
|
|
58
|
+
provider: ContractProvider,
|
|
59
|
+
sourceChainSelector: bigint,
|
|
60
|
+
): Promise<SourceChainConfig> {
|
|
61
|
+
const result = await provider.get('sourceChainConfig', [
|
|
62
|
+
{ type: 'int', value: sourceChainSelector },
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
// Tolk returns struct as tuple
|
|
66
|
+
const router = result.stack.readAddress()
|
|
67
|
+
const isEnabled = result.stack.readBoolean()
|
|
68
|
+
const minSeqNr = result.stack.readBigNumber()
|
|
69
|
+
const isRMNVerificationDisabled = result.stack.readBoolean()
|
|
70
|
+
|
|
71
|
+
// onRamp is stored as CrossChainAddress cell
|
|
72
|
+
const onRampCell = result.stack.readCell()
|
|
73
|
+
const onRampSlice = onRampCell.beginParse()
|
|
74
|
+
|
|
75
|
+
// Check if length-prefixed or raw format based on cell bit length
|
|
76
|
+
const cellBits = onRampCell.bits.length
|
|
77
|
+
let onRamp: Buffer
|
|
78
|
+
|
|
79
|
+
if (cellBits === 160) {
|
|
80
|
+
// Raw 20-byte EVM address (no length prefix)
|
|
81
|
+
onRamp = onRampSlice.loadBuffer(20)
|
|
82
|
+
} else {
|
|
83
|
+
// Length-prefixed format: 8-bit length + data
|
|
84
|
+
const onRampLength = onRampSlice.loadUint(8)
|
|
85
|
+
onRamp = onRampSlice.loadBuffer(onRampLength)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
router,
|
|
90
|
+
isEnabled,
|
|
91
|
+
minSeqNr,
|
|
92
|
+
isRMNVerificationDisabled,
|
|
93
|
+
onRamp,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|