@chainlink/ccip-sdk 0.91.1 → 0.92.1
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 +176 -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 +11 -9
- 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 +15 -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 +325 -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
package/src/sui/index.ts
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toHex } from '@mysten/bcs'
|
|
2
|
+
import { type SuiTransactionBlockResponse, SuiClient } from '@mysten/sui/client'
|
|
3
|
+
import type { Keypair } from '@mysten/sui/cryptography'
|
|
4
|
+
import { SuiGraphQLClient } from '@mysten/sui/graphql'
|
|
5
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
6
|
+
import { type BytesLike, AbiCoder, hexlify, isBytesLike } from 'ethers'
|
|
2
7
|
import type { PickDeep } from 'type-fest'
|
|
3
8
|
|
|
4
9
|
import { AptosChain } from '../aptos/index.ts'
|
|
5
10
|
import { type LogFilter, Chain } from '../chain.ts'
|
|
6
|
-
import
|
|
11
|
+
import {
|
|
12
|
+
CCIPContractNotRouterError,
|
|
13
|
+
CCIPDataFormatUnsupportedError,
|
|
14
|
+
CCIPError,
|
|
15
|
+
CCIPErrorCode,
|
|
16
|
+
CCIPExecTxRevertedError,
|
|
17
|
+
CCIPExtraArgsInvalidError,
|
|
18
|
+
CCIPNotImplementedError,
|
|
19
|
+
CCIPSuiMessageVersionInvalidError,
|
|
20
|
+
CCIPVersionFeatureUnavailableError,
|
|
21
|
+
} from '../errors/index.ts'
|
|
22
|
+
import type { ExtraArgs, SuiExtraArgsV1 } from '../extra-args.ts'
|
|
7
23
|
import { getSuiLeafHasher } from './hasher.ts'
|
|
8
24
|
import type { LeafHasher } from '../hasher/common.ts'
|
|
9
25
|
import { supportedChains } from '../supported-chains.ts'
|
|
10
26
|
import {
|
|
11
27
|
type AnyMessage,
|
|
28
|
+
type CCIPMessage,
|
|
12
29
|
type CCIPRequest,
|
|
30
|
+
type CCIPVersion,
|
|
13
31
|
type ChainTransaction,
|
|
14
32
|
type CommitReport,
|
|
15
33
|
type ExecutionReceipt,
|
|
16
34
|
type ExecutionReport,
|
|
35
|
+
type ExecutionState,
|
|
17
36
|
type Lane,
|
|
18
37
|
type Log_,
|
|
19
38
|
type NetworkInfo,
|
|
@@ -22,6 +41,30 @@ import {
|
|
|
22
41
|
ChainFamily,
|
|
23
42
|
} from '../types.ts'
|
|
24
43
|
import type { CCIPMessage_V1_6_Sui } from './types.ts'
|
|
44
|
+
import { bytesToBuffer, decodeAddress, getDataBytes, networkInfo } from '../utils.ts'
|
|
45
|
+
import { type CommitEvent, getSuiEventsInTimeRange } from './events.ts'
|
|
46
|
+
import {
|
|
47
|
+
type SuiManuallyExecuteInput,
|
|
48
|
+
type TokenConfig,
|
|
49
|
+
buildManualExecutionPTB,
|
|
50
|
+
} from './manuallyExec/index.ts'
|
|
51
|
+
import {
|
|
52
|
+
fetchTokenConfigs,
|
|
53
|
+
getCcipObjectRef,
|
|
54
|
+
getOffRampStateObject,
|
|
55
|
+
getReceiverModule,
|
|
56
|
+
} from './objects.ts'
|
|
57
|
+
import selectors from '../selectors.ts'
|
|
58
|
+
import { discoverCCIP, discoverOfframp } from './discovery.ts'
|
|
59
|
+
|
|
60
|
+
export const SUI_EXTRA_ARGS_V1_TAG = '21ea4ca9' as const
|
|
61
|
+
|
|
62
|
+
type SuiContractDir = {
|
|
63
|
+
ccip?: string
|
|
64
|
+
onRamp?: string
|
|
65
|
+
offRamp?: string
|
|
66
|
+
router?: string
|
|
67
|
+
}
|
|
25
68
|
|
|
26
69
|
/**
|
|
27
70
|
* Sui chain implementation supporting Sui networks.
|
|
@@ -32,119 +75,353 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
32
75
|
supportedChains[ChainFamily.Sui] = SuiChain
|
|
33
76
|
}
|
|
34
77
|
static readonly family = ChainFamily.Sui
|
|
35
|
-
static readonly decimals =
|
|
78
|
+
static readonly decimals = 9 // SUI has 9 decimals
|
|
79
|
+
|
|
80
|
+
override readonly network: NetworkInfo<typeof ChainFamily.Sui>
|
|
81
|
+
readonly client: SuiClient
|
|
82
|
+
readonly graphqlClient: SuiGraphQLClient
|
|
83
|
+
|
|
84
|
+
// contracts dir <chainSelectorName, SuiContractDir>
|
|
85
|
+
readonly contractsDir: SuiContractDir
|
|
36
86
|
|
|
37
87
|
/**
|
|
38
88
|
* Creates a new SuiChain instance.
|
|
39
|
-
* @param
|
|
89
|
+
* @param client - Sui client for interacting with the Sui network.
|
|
90
|
+
* @param network - Network information for this chain.
|
|
40
91
|
*/
|
|
41
|
-
constructor(network: NetworkInfo<typeof ChainFamily.Sui>, ctx?: WithLogger) {
|
|
92
|
+
constructor(client: SuiClient, network: NetworkInfo<typeof ChainFamily.Sui>, ctx?: WithLogger) {
|
|
42
93
|
super(network, ctx)
|
|
94
|
+
|
|
95
|
+
this.client = client
|
|
96
|
+
this.network = network
|
|
97
|
+
this.contractsDir = {}
|
|
98
|
+
|
|
99
|
+
// TODO: Graphql client should come from config
|
|
100
|
+
let graphqlUrl: string
|
|
101
|
+
const selector = network.chainSelector
|
|
102
|
+
if (selector === selectors['sui:1'].selector) {
|
|
103
|
+
// Sui mainnet (sui:1)
|
|
104
|
+
graphqlUrl = 'https://graphql.mainnet.sui.io/graphql'
|
|
105
|
+
} else if (selector === selectors['sui:2'].selector) {
|
|
106
|
+
// Sui testnet (sui:2)
|
|
107
|
+
graphqlUrl = 'https://graphql.testnet.sui.io/graphql'
|
|
108
|
+
} else {
|
|
109
|
+
// Localnet (sui:4) or unknown
|
|
110
|
+
graphqlUrl = 'https://graphql.devnet.sui.io/graphql'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.graphqlClient = new SuiGraphQLClient({
|
|
114
|
+
url: graphqlUrl,
|
|
115
|
+
})
|
|
43
116
|
}
|
|
44
117
|
|
|
45
118
|
/**
|
|
46
119
|
* Creates a SuiChain instance from an RPC URL.
|
|
47
|
-
* @param
|
|
120
|
+
* @param url - HTTP or WebSocket endpoint URL for the Sui network.
|
|
48
121
|
* @returns A new SuiChain instance.
|
|
49
122
|
*/
|
|
50
|
-
static async fromUrl(
|
|
51
|
-
|
|
123
|
+
static async fromUrl(url: string, ctx?: WithLogger): Promise<SuiChain> {
|
|
124
|
+
const client = new SuiClient({ url })
|
|
125
|
+
|
|
126
|
+
// Get chain identifier from the client and map to network info format
|
|
127
|
+
const rawChainId = await client.getChainIdentifier().catch(() => null)
|
|
128
|
+
if (rawChainId === null) {
|
|
129
|
+
throw new CCIPDataFormatUnsupportedError(`Unable to fetch chain identifier from URL: ${url}`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Map Sui chain identifiers to our network info format
|
|
133
|
+
// Reference: https://docs.sui.io/guides/developer/getting-started/connect
|
|
134
|
+
let chainId: string
|
|
135
|
+
if (rawChainId === '35834a8a') {
|
|
136
|
+
chainId = 'sui:1' // mainnet
|
|
137
|
+
} else if (rawChainId === '4c78adac') {
|
|
138
|
+
chainId = 'sui:2' // testnet
|
|
139
|
+
} else if (rawChainId === 'b0c08dea') {
|
|
140
|
+
chainId = 'sui:4' // devnet
|
|
141
|
+
} else {
|
|
142
|
+
throw new CCIPError(
|
|
143
|
+
CCIPErrorCode.NETWORK_FAMILY_UNSUPPORTED,
|
|
144
|
+
`Unsupported Sui chain identifier: ${rawChainId}`,
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const network = networkInfo(chainId) as NetworkInfo<typeof ChainFamily.Sui>
|
|
149
|
+
return new SuiChain(client, network, ctx)
|
|
52
150
|
}
|
|
53
151
|
|
|
54
152
|
/** {@inheritDoc Chain.getBlockTimestamp} */
|
|
55
|
-
async getBlockTimestamp(
|
|
56
|
-
|
|
153
|
+
async getBlockTimestamp(block: number): Promise<number> {
|
|
154
|
+
const checkpoint = await this.client.getCheckpoint({
|
|
155
|
+
id: String(block),
|
|
156
|
+
})
|
|
157
|
+
return Number(checkpoint.timestampMs) / 1000
|
|
57
158
|
}
|
|
58
159
|
|
|
59
160
|
/** {@inheritDoc Chain.getTransaction} */
|
|
60
|
-
async getTransaction(
|
|
61
|
-
|
|
161
|
+
async getTransaction(hash: string | number): Promise<ChainTransaction> {
|
|
162
|
+
// For Sui, hash should be a transaction digest (string)
|
|
163
|
+
const digest = typeof hash === 'number' ? String(hash) : hash
|
|
164
|
+
|
|
165
|
+
const txResponse = await this.client.getTransactionBlock({
|
|
166
|
+
digest,
|
|
167
|
+
options: {
|
|
168
|
+
showEvents: true,
|
|
169
|
+
showEffects: true,
|
|
170
|
+
showInput: true,
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Extract events from the transaction
|
|
175
|
+
const events: Log_[] = []
|
|
176
|
+
if (txResponse.events) {
|
|
177
|
+
for (let i = 0; i < txResponse.events.length; i++) {
|
|
178
|
+
const event = txResponse.events[i]
|
|
179
|
+
const eventType = event.type
|
|
180
|
+
const packageId = eventType.split('::')[0]
|
|
181
|
+
const moduleName = eventType.split('::')[1]
|
|
182
|
+
const eventName = eventType.split('::')[2]
|
|
183
|
+
|
|
184
|
+
events.push({
|
|
185
|
+
address: `${packageId}::${moduleName}`,
|
|
186
|
+
transactionHash: digest,
|
|
187
|
+
index: i,
|
|
188
|
+
blockNumber: Number(txResponse.checkpoint || 0),
|
|
189
|
+
data: event.parsedJson as Record<string, unknown>,
|
|
190
|
+
topics: [eventName],
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
hash: digest,
|
|
197
|
+
logs: events,
|
|
198
|
+
blockNumber: Number(txResponse.checkpoint || 0),
|
|
199
|
+
timestamp: Number(txResponse.timestampMs || 0) / 1000,
|
|
200
|
+
from: txResponse.transaction?.data?.sender || '',
|
|
201
|
+
}
|
|
62
202
|
}
|
|
63
203
|
|
|
64
204
|
/** {@inheritDoc Chain.getLogs} */
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
205
|
+
async *getLogs(opts: LogFilter & { versionAsHash?: boolean }) {
|
|
206
|
+
if (!this.contractsDir.offRamp) {
|
|
207
|
+
throw new CCIPContractNotRouterError('OffRamp address not set in contracts directory', 'Sui')
|
|
208
|
+
}
|
|
209
|
+
// Extract the event type from topics
|
|
210
|
+
const topic = Array.isArray(opts.topics?.[0]) ? opts.topics[0][0] : opts.topics?.[0] || ''
|
|
211
|
+
if (!topic || topic !== 'CommitReportAccepted') {
|
|
212
|
+
throw new CCIPVersionFeatureUnavailableError(
|
|
213
|
+
'Event type',
|
|
214
|
+
topic || 'unknown',
|
|
215
|
+
'CommitReportAccepted',
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const startTime = opts.startTime ? new Date(opts.startTime * 1000) : new Date(0)
|
|
220
|
+
const endTime = opts.endBlock
|
|
221
|
+
? new Date(opts.endBlock)
|
|
222
|
+
: new Date(startTime.getTime() + 1 * 24 * 60 * 60 * 1000) // default to +24h
|
|
223
|
+
|
|
224
|
+
this.logger.info(
|
|
225
|
+
`Fetching Sui events of type ${topic} from ${startTime.toISOString()} to ${endTime.toISOString()}`,
|
|
226
|
+
)
|
|
227
|
+
const events = await getSuiEventsInTimeRange<CommitEvent>(
|
|
228
|
+
this.client,
|
|
229
|
+
this.graphqlClient,
|
|
230
|
+
`${this.contractsDir.offRamp}::offramp::CommitReportAccepted`,
|
|
231
|
+
startTime,
|
|
232
|
+
endTime,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
for (const event of events) {
|
|
236
|
+
const eventData = event.contents.json
|
|
237
|
+
yield {
|
|
238
|
+
address: this.contractsDir.offRamp,
|
|
239
|
+
transactionHash: event.transaction?.digest || '',
|
|
240
|
+
index: 0, // Sui events do not have an index, set to 0
|
|
241
|
+
blockNumber: Number(event.transaction?.effects.checkpoint.sequenceNumber || 0),
|
|
242
|
+
data: eventData,
|
|
243
|
+
topics: [topic],
|
|
244
|
+
}
|
|
245
|
+
}
|
|
69
246
|
}
|
|
70
247
|
|
|
71
248
|
/** {@inheritDoc Chain.fetchRequestsInTx} */
|
|
72
249
|
override async fetchRequestsInTx(_tx: string | ChainTransaction): Promise<CCIPRequest[]> {
|
|
73
|
-
return Promise.reject(new
|
|
250
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.fetchRequestsInTx'))
|
|
74
251
|
}
|
|
75
252
|
|
|
76
253
|
/** {@inheritDoc Chain.fetchAllMessagesInBatch} */
|
|
77
254
|
override async fetchAllMessagesInBatch<
|
|
78
255
|
R extends PickDeep<
|
|
79
256
|
CCIPRequest,
|
|
80
|
-
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.
|
|
257
|
+
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
|
|
81
258
|
>,
|
|
82
259
|
>(
|
|
83
260
|
_request: R,
|
|
84
261
|
_commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
|
|
85
262
|
_opts?: { page?: number },
|
|
86
263
|
): Promise<R['message'][]> {
|
|
87
|
-
return Promise.reject(new
|
|
264
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.fetchAllMessagesInBatch'))
|
|
88
265
|
}
|
|
89
266
|
|
|
90
267
|
/** {@inheritDoc Chain.typeAndVersion} */
|
|
91
|
-
async typeAndVersion(
|
|
92
|
-
|
|
93
|
-
): Promise<
|
|
94
|
-
| [type_: string, version: string, typeAndVersion: string]
|
|
95
|
-
| [type_: string, version: string, typeAndVersion: string, suffix: string]
|
|
96
|
-
> {
|
|
97
|
-
return Promise.reject(new Error('Not implemented'))
|
|
268
|
+
async typeAndVersion(_address: string) {
|
|
269
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.typeAndVersion'))
|
|
98
270
|
}
|
|
99
271
|
|
|
100
272
|
/** {@inheritDoc Chain.getRouterForOnRamp} */
|
|
101
|
-
getRouterForOnRamp(
|
|
102
|
-
|
|
273
|
+
async getRouterForOnRamp(onRamp: string, _destChainSelector: bigint): Promise<string> {
|
|
274
|
+
this.contractsDir.onRamp = onRamp
|
|
275
|
+
if (onRamp !== this.contractsDir.onRamp) {
|
|
276
|
+
this.contractsDir.onRamp = onRamp
|
|
277
|
+
}
|
|
278
|
+
return Promise.resolve(this.contractsDir.onRamp)
|
|
103
279
|
}
|
|
104
280
|
|
|
105
281
|
/** {@inheritDoc Chain.getRouterForOffRamp} */
|
|
106
|
-
getRouterForOffRamp(
|
|
107
|
-
|
|
282
|
+
getRouterForOffRamp(offRamp: string, _sourceChainSelector: bigint): Promise<string> {
|
|
283
|
+
throw new CCIPContractNotRouterError(offRamp, 'unknown')
|
|
108
284
|
}
|
|
109
285
|
|
|
110
286
|
/** {@inheritDoc Chain.getNativeTokenForRouter} */
|
|
111
287
|
getNativeTokenForRouter(_router: string): Promise<string> {
|
|
112
|
-
|
|
288
|
+
// SUI native token is always 0x2::sui::SUI
|
|
289
|
+
return Promise.resolve('0x2::sui::SUI')
|
|
113
290
|
}
|
|
114
291
|
|
|
115
292
|
/** {@inheritDoc Chain.getOffRampsForRouter} */
|
|
116
|
-
getOffRampsForRouter(
|
|
117
|
-
|
|
293
|
+
async getOffRampsForRouter(router: string, _sourceChainSelector: bigint): Promise<string[]> {
|
|
294
|
+
const ccip = await discoverCCIP(this.client, router)
|
|
295
|
+
const offramp = await discoverOfframp(this.client, ccip)
|
|
296
|
+
this.contractsDir.offRamp = offramp
|
|
297
|
+
this.contractsDir.ccip = ccip
|
|
298
|
+
return [offramp]
|
|
118
299
|
}
|
|
119
300
|
|
|
120
301
|
/** {@inheritDoc Chain.getOnRampForRouter} */
|
|
121
302
|
getOnRampForRouter(_router: string, _destChainSelector: bigint): Promise<string> {
|
|
122
|
-
|
|
303
|
+
if (!this.contractsDir.onRamp) {
|
|
304
|
+
throw new CCIPContractNotRouterError('OnRamp address not set in contracts directory', 'Sui')
|
|
305
|
+
}
|
|
306
|
+
return Promise.resolve(this.contractsDir.onRamp)
|
|
123
307
|
}
|
|
124
308
|
|
|
125
309
|
/** {@inheritDoc Chain.getOnRampForOffRamp} */
|
|
126
|
-
async getOnRampForOffRamp(
|
|
127
|
-
|
|
310
|
+
async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
311
|
+
if (!this.contractsDir.ccip) {
|
|
312
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'CCIP address not set in contracts directory')
|
|
313
|
+
}
|
|
314
|
+
const offrampPackageId = offRamp
|
|
315
|
+
const functionName = 'get_source_chain_config'
|
|
316
|
+
const target = `${offrampPackageId}::offramp::${functionName}`
|
|
317
|
+
|
|
318
|
+
// Get the OffRampState object
|
|
319
|
+
const offrampStateObject = await getOffRampStateObject(this.client, offrampPackageId)
|
|
320
|
+
const ccipObjectRef = await getCcipObjectRef(this.client, this.contractsDir.ccip)
|
|
321
|
+
// Use the Transaction builder to create a move call
|
|
322
|
+
const tx = new Transaction()
|
|
323
|
+
|
|
324
|
+
// Add move call to the transaction with OffRampState object and source chain selector
|
|
325
|
+
tx.moveCall({
|
|
326
|
+
target,
|
|
327
|
+
arguments: [
|
|
328
|
+
tx.object(ccipObjectRef),
|
|
329
|
+
tx.object(offrampStateObject),
|
|
330
|
+
tx.pure.u64(sourceChainSelector),
|
|
331
|
+
],
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
// Execute with devInspectTransactionBlock for read-only call
|
|
335
|
+
const result = await this.client.devInspectTransactionBlock({
|
|
336
|
+
sender: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
337
|
+
transactionBlock: tx,
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
if (result.effects.status.status !== 'success' || !result.results?.[0]?.returnValues?.[0]) {
|
|
341
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
342
|
+
`Failed to call ${target}: ${result.effects.status.error || 'No return value'}`,
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// The return value is a SourceChainConfig struct with the following fields:
|
|
347
|
+
// - Router (address = 32 bytes)
|
|
348
|
+
// - IsEnabled (bool = 1 byte)
|
|
349
|
+
// - MinSeqNr (u64 = 8 bytes)
|
|
350
|
+
// - IsRmnVerificationDisabled (bool = 1 byte)
|
|
351
|
+
// - OnRamp (vector<u8> = length + bytes)
|
|
352
|
+
const returnValue = result.results[0].returnValues[0]
|
|
353
|
+
const [data] = returnValue
|
|
354
|
+
const configBytes = new Uint8Array(data)
|
|
355
|
+
|
|
356
|
+
let offset = 0
|
|
357
|
+
|
|
358
|
+
// Skip Router (32 bytes)
|
|
359
|
+
offset += 32
|
|
360
|
+
|
|
361
|
+
// Skip IsEnabled (1 byte)
|
|
362
|
+
offset += 1
|
|
363
|
+
|
|
364
|
+
// Skip MinSeqNr (8 bytes)
|
|
365
|
+
offset += 8
|
|
366
|
+
|
|
367
|
+
// Skip IsRmnVerificationDisabled (1 byte)
|
|
368
|
+
offset += 1
|
|
369
|
+
|
|
370
|
+
// OnRamp (vector<u8>)
|
|
371
|
+
const onRampLength = configBytes[offset]
|
|
372
|
+
offset += 1
|
|
373
|
+
const onRampBytes = configBytes.slice(offset, offset + onRampLength)
|
|
374
|
+
|
|
375
|
+
// Decode the address from the onRamp bytes
|
|
376
|
+
return decodeAddress(onRampBytes, networkInfo(sourceChainSelector).family)
|
|
128
377
|
}
|
|
129
378
|
|
|
130
379
|
/** {@inheritDoc Chain.getCommitStoreForOffRamp} */
|
|
131
|
-
getCommitStoreForOffRamp(
|
|
132
|
-
return Promise.
|
|
380
|
+
getCommitStoreForOffRamp(offRamp: string): Promise<string> {
|
|
381
|
+
return Promise.resolve(offRamp)
|
|
133
382
|
}
|
|
134
383
|
|
|
135
384
|
/** {@inheritDoc Chain.getTokenForTokenPool} */
|
|
136
|
-
|
|
137
|
-
|
|
385
|
+
getTokenForTokenPool(_tokenPool: string): Promise<string> {
|
|
386
|
+
throw new CCIPNotImplementedError()
|
|
138
387
|
}
|
|
139
388
|
|
|
140
389
|
/** {@inheritDoc Chain.getTokenInfo} */
|
|
141
|
-
async getTokenInfo(
|
|
142
|
-
|
|
390
|
+
async getTokenInfo(token: string): Promise<{ symbol: string; decimals: number }> {
|
|
391
|
+
// Handle native SUI token
|
|
392
|
+
if (token === '0x2::sui::SUI' || token.includes('::sui::SUI')) {
|
|
393
|
+
return { symbol: 'SUI', decimals: 9 }
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
// For Coin types, try to fetch metadata from the coin metadata object
|
|
398
|
+
// Format: 0xPACKAGE::module::TYPE
|
|
399
|
+
const coinMetadata = await this.client.getCoinMetadata({ coinType: token })
|
|
400
|
+
|
|
401
|
+
if (coinMetadata) {
|
|
402
|
+
return {
|
|
403
|
+
symbol: coinMetadata.symbol || 'UNKNOWN',
|
|
404
|
+
decimals: coinMetadata.decimals,
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.log(`Failed to fetch coin metadata for ${token}:`, error)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Fallback: parse from token type string if possible
|
|
412
|
+
const parts = token.split('::')
|
|
413
|
+
const symbol = parts[parts.length - 1] || 'UNKNOWN'
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
symbol: symbol.toUpperCase(),
|
|
417
|
+
decimals: 9, // Default to 9 decimals (SUI standard)
|
|
418
|
+
}
|
|
143
419
|
}
|
|
144
420
|
|
|
421
|
+
/** {@inheritDoc Chain.getTokenAdminRegistryFor} */
|
|
145
422
|
/** {@inheritDoc Chain.getTokenAdminRegistryFor} */
|
|
146
423
|
getTokenAdminRegistryFor(_address: string): Promise<string> {
|
|
147
|
-
return Promise.reject(new
|
|
424
|
+
return Promise.reject(new CCIPNotImplementedError())
|
|
148
425
|
}
|
|
149
426
|
|
|
150
427
|
// Static methods for decoding
|
|
@@ -154,7 +431,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
154
431
|
* @returns Decoded CCIPMessage or undefined if not valid.
|
|
155
432
|
*/
|
|
156
433
|
static decodeMessage(_log: Log_): CCIPMessage_V1_6_Sui | undefined {
|
|
157
|
-
throw new
|
|
434
|
+
throw new CCIPNotImplementedError()
|
|
158
435
|
}
|
|
159
436
|
|
|
160
437
|
/**
|
|
@@ -162,41 +439,120 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
162
439
|
* @param extraArgs - Encoded extra arguments bytes.
|
|
163
440
|
* @returns Decoded extra arguments or undefined if unknown format.
|
|
164
441
|
*/
|
|
165
|
-
static decodeExtraArgs(
|
|
166
|
-
extraArgs
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
442
|
+
static decodeExtraArgs(extraArgs: BytesLike): SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' } {
|
|
443
|
+
const data = getDataBytes(extraArgs)
|
|
444
|
+
const hexBytes = toHex(data)
|
|
445
|
+
if (!hexBytes.startsWith(SUI_EXTRA_ARGS_V1_TAG)) {
|
|
446
|
+
throw new CCIPExtraArgsInvalidError('Sui', hexBytes)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const abiData = '0x' + hexBytes.slice(8)
|
|
450
|
+
const decoded = AbiCoder.defaultAbiCoder().decode(
|
|
451
|
+
['tuple(uint256,bool,bytes32,bytes32[])'],
|
|
452
|
+
abiData,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
const tuple = decoded[0] as readonly [bigint, boolean, string, string[]]
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
gasLimit: tuple[0],
|
|
459
|
+
allowOutOfOrderExecution: tuple[1],
|
|
460
|
+
tokenReceiver: tuple[2],
|
|
461
|
+
receiverObjectIds: tuple[3], // Already an array of hex strings
|
|
462
|
+
_tag: 'SuiExtraArgsV1',
|
|
463
|
+
}
|
|
172
464
|
}
|
|
173
465
|
|
|
174
466
|
/**
|
|
175
|
-
* Encodes extra arguments for
|
|
176
|
-
* @param
|
|
177
|
-
* @returns Encoded extra arguments as hex string.
|
|
467
|
+
* Encodes extra arguments for CCIP messages.
|
|
468
|
+
* @param _extraArgs - Extra arguments to encode.
|
|
469
|
+
* @returns Encoded extra arguments as a hex string.
|
|
178
470
|
*/
|
|
179
|
-
static encodeExtraArgs(
|
|
180
|
-
|
|
471
|
+
static encodeExtraArgs(_extraArgs: ExtraArgs): string {
|
|
472
|
+
throw new CCIPNotImplementedError()
|
|
181
473
|
}
|
|
182
474
|
|
|
183
475
|
/**
|
|
184
|
-
* Decodes commit reports from a
|
|
185
|
-
* @param
|
|
186
|
-
* @param _lane -
|
|
187
|
-
* @returns Array of
|
|
476
|
+
* Decodes commit reports from a log entry.
|
|
477
|
+
* @param log - The log entry to decode.
|
|
478
|
+
* @param _lane - Optional lane information.
|
|
479
|
+
* @returns Array of decoded commit reports or undefined.
|
|
188
480
|
*/
|
|
189
|
-
static decodeCommits(
|
|
190
|
-
|
|
481
|
+
static decodeCommits(log: Log_, _lane?: Lane): CommitReport[] | undefined {
|
|
482
|
+
if (!log.data || typeof log.data !== 'object' || !('unblessed_merkle_roots' in log.data)) {
|
|
483
|
+
return
|
|
484
|
+
}
|
|
485
|
+
const toHexFromBase64 = (b64: string) => '0x' + Buffer.from(b64, 'base64').toString('hex')
|
|
486
|
+
|
|
487
|
+
const eventData = log.data as CommitEvent
|
|
488
|
+
const unblessedRoots = eventData.unblessed_merkle_roots
|
|
489
|
+
if (!Array.isArray(unblessedRoots) || unblessedRoots.length === 0) {
|
|
490
|
+
return
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return unblessedRoots.map((root) => {
|
|
494
|
+
return {
|
|
495
|
+
sourceChainSelector: BigInt(root.source_chain_selector),
|
|
496
|
+
onRampAddress: toHexFromBase64(root.on_ramp_address),
|
|
497
|
+
minSeqNr: BigInt(root.min_seq_nr),
|
|
498
|
+
maxSeqNr: BigInt(root.max_seq_nr),
|
|
499
|
+
merkleRoot: toHexFromBase64(root.merkle_root),
|
|
500
|
+
}
|
|
501
|
+
})
|
|
191
502
|
}
|
|
192
503
|
|
|
193
504
|
/**
|
|
194
|
-
* Decodes an execution receipt from a
|
|
195
|
-
* @param
|
|
196
|
-
* @returns
|
|
505
|
+
* Decodes an execution receipt from a log entry.
|
|
506
|
+
* @param log - The log entry to decode.
|
|
507
|
+
* @returns Decoded execution receipt or undefined.
|
|
197
508
|
*/
|
|
198
|
-
static decodeReceipt(
|
|
199
|
-
|
|
509
|
+
static decodeReceipt(log: Log_): ExecutionReceipt | undefined {
|
|
510
|
+
// Check if this is an ExecutionStateChanged event
|
|
511
|
+
const topic = (Array.isArray(log.topics) ? log.topics[0] : log.topics) as string
|
|
512
|
+
if (topic !== 'ExecutionStateChanged') {
|
|
513
|
+
return undefined
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Validate log data structure
|
|
517
|
+
if (!log.data || typeof log.data !== 'object') {
|
|
518
|
+
return undefined
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const eventData = log.data as {
|
|
522
|
+
message_hash?: number[]
|
|
523
|
+
message_id?: number[]
|
|
524
|
+
sequence_number?: string
|
|
525
|
+
source_chain_selector?: string
|
|
526
|
+
state?: number
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Verify required fields exist
|
|
530
|
+
if (
|
|
531
|
+
!eventData.message_id ||
|
|
532
|
+
!Array.isArray(eventData.message_id) ||
|
|
533
|
+
eventData.sequence_number === undefined ||
|
|
534
|
+
eventData.state === undefined
|
|
535
|
+
) {
|
|
536
|
+
return undefined
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const toHex = (bytes: BytesLike | number[]) => hexlify(bytesToBuffer(bytes))
|
|
540
|
+
|
|
541
|
+
// Convert message_id bytes array to hex string
|
|
542
|
+
const messageId = toHex(eventData.message_id)
|
|
543
|
+
|
|
544
|
+
// Convert message_hash bytes array to hex string (if present)
|
|
545
|
+
const messageHash = eventData.message_hash ? toHex(eventData.message_hash) : undefined
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
messageId,
|
|
549
|
+
sequenceNumber: BigInt(eventData.sequence_number),
|
|
550
|
+
state: eventData.state as ExecutionState,
|
|
551
|
+
sourceChainSelector: eventData.source_chain_selector
|
|
552
|
+
? BigInt(eventData.source_chain_selector)
|
|
553
|
+
: undefined,
|
|
554
|
+
messageHash,
|
|
555
|
+
}
|
|
200
556
|
}
|
|
201
557
|
|
|
202
558
|
/**
|
|
@@ -208,6 +564,13 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
208
564
|
return AptosChain.getAddress(bytes)
|
|
209
565
|
}
|
|
210
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Validates a transaction hash format for Sui
|
|
569
|
+
*/
|
|
570
|
+
static isTxHash(_v: unknown): _v is string {
|
|
571
|
+
return false
|
|
572
|
+
}
|
|
573
|
+
|
|
211
574
|
/**
|
|
212
575
|
* Gets the leaf hasher for Sui destination chains.
|
|
213
576
|
* @param lane - Lane configuration.
|
|
@@ -219,7 +582,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
219
582
|
|
|
220
583
|
/** {@inheritDoc Chain.getFee} */
|
|
221
584
|
async getFee(_router: string, _destChainSelector: bigint, _message: AnyMessage): Promise<bigint> {
|
|
222
|
-
return Promise.reject(new
|
|
585
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getFee'))
|
|
223
586
|
}
|
|
224
587
|
|
|
225
588
|
/** {@inheritDoc Chain.generateUnsignedSendMessage} */
|
|
@@ -230,7 +593,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
230
593
|
_message: AnyMessage & { fee?: bigint },
|
|
231
594
|
_opts?: { approveMax?: boolean },
|
|
232
595
|
): Promise<never> {
|
|
233
|
-
return Promise.reject(new
|
|
596
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.generateUnsignedSendMessage'))
|
|
234
597
|
}
|
|
235
598
|
|
|
236
599
|
/** {@inheritDoc Chain.sendMessage} */
|
|
@@ -240,13 +603,13 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
240
603
|
_message: AnyMessage & { fee: bigint },
|
|
241
604
|
_opts?: { wallet?: unknown; approveMax?: boolean },
|
|
242
605
|
): Promise<CCIPRequest> {
|
|
243
|
-
return Promise.reject(new
|
|
606
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.sendMessage'))
|
|
244
607
|
}
|
|
245
608
|
|
|
246
609
|
/** {@inheritDoc Chain.fetchOffchainTokenData} */
|
|
247
610
|
fetchOffchainTokenData(request: CCIPRequest): Promise<OffchainTokenData[]> {
|
|
248
611
|
if (!('receiverObjectIds' in request.message)) {
|
|
249
|
-
throw new
|
|
612
|
+
throw new CCIPSuiMessageVersionInvalidError()
|
|
250
613
|
}
|
|
251
614
|
// default offchain token data
|
|
252
615
|
return Promise.resolve(request.message.tokenAmounts.map(() => undefined))
|
|
@@ -259,16 +622,101 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
259
622
|
_execReport: ExecutionReport,
|
|
260
623
|
_opts: object,
|
|
261
624
|
): Promise<never> {
|
|
262
|
-
return Promise.reject(new
|
|
625
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.generateUnsignedExecuteReport'))
|
|
263
626
|
}
|
|
264
627
|
|
|
265
628
|
/** {@inheritDoc Chain.executeReport} */
|
|
266
629
|
async executeReport(
|
|
267
630
|
_offRamp: string,
|
|
268
|
-
|
|
269
|
-
|
|
631
|
+
execReport: ExecutionReport,
|
|
632
|
+
opts: { wallet: unknown; gasLimit?: number; receiverObjectIds?: string[] },
|
|
270
633
|
): Promise<ChainTransaction> {
|
|
271
|
-
|
|
634
|
+
if (!this.contractsDir.offRamp || !this.contractsDir.ccip) {
|
|
635
|
+
throw new CCIPContractNotRouterError(
|
|
636
|
+
'OffRamp or CCIP address not set in contracts directory',
|
|
637
|
+
'Sui',
|
|
638
|
+
)
|
|
639
|
+
}
|
|
640
|
+
const wallet = opts.wallet as Keypair
|
|
641
|
+
const ccipObjectRef = await getCcipObjectRef(this.client, this.contractsDir.ccip)
|
|
642
|
+
const offrampStateObject = await getOffRampStateObject(this.client, this.contractsDir.offRamp)
|
|
643
|
+
const receiverConfig = await getReceiverModule(
|
|
644
|
+
this.client,
|
|
645
|
+
this.contractsDir.ccip,
|
|
646
|
+
ccipObjectRef,
|
|
647
|
+
execReport.message.receiver,
|
|
648
|
+
)
|
|
649
|
+
let tokenConfigs: TokenConfig[] = []
|
|
650
|
+
if (execReport.message.tokenAmounts.length !== 0) {
|
|
651
|
+
tokenConfigs = await fetchTokenConfigs(
|
|
652
|
+
this.client,
|
|
653
|
+
this.contractsDir.ccip,
|
|
654
|
+
ccipObjectRef,
|
|
655
|
+
execReport.message.tokenAmounts as CCIPMessage<typeof CCIPVersion.V1_6>['tokenAmounts'],
|
|
656
|
+
)
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const input: SuiManuallyExecuteInput = {
|
|
660
|
+
executionReport: execReport as ExecutionReport<CCIPMessage_V1_6_Sui>,
|
|
661
|
+
offrampAddress: this.contractsDir.offRamp,
|
|
662
|
+
ccipAddress: this.contractsDir.ccip,
|
|
663
|
+
ccipObjectRef,
|
|
664
|
+
offrampStateObject,
|
|
665
|
+
receiverConfig,
|
|
666
|
+
tokenConfigs,
|
|
667
|
+
}
|
|
668
|
+
if (opts.receiverObjectIds) {
|
|
669
|
+
this.logger.info(
|
|
670
|
+
`Overriding Sui Manual Execution receiverObjectIds with: ${opts.receiverObjectIds.join(', ')}`,
|
|
671
|
+
)
|
|
672
|
+
input.overrideReceiverObjectIds = opts.receiverObjectIds
|
|
673
|
+
}
|
|
674
|
+
const tx = buildManualExecutionPTB(input)
|
|
675
|
+
|
|
676
|
+
// Set gas budget if provided
|
|
677
|
+
if (opts?.gasLimit) {
|
|
678
|
+
tx.setGasBudget(opts.gasLimit)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
this.logger.info(`Executing Sui CCIP executeReport transaction...`)
|
|
682
|
+
// Sign and execute the transaction
|
|
683
|
+
let result: SuiTransactionBlockResponse
|
|
684
|
+
try {
|
|
685
|
+
result = await this.client.signAndExecuteTransaction({
|
|
686
|
+
signer: wallet,
|
|
687
|
+
transaction: tx,
|
|
688
|
+
options: {
|
|
689
|
+
showEffects: true,
|
|
690
|
+
showEvents: true,
|
|
691
|
+
},
|
|
692
|
+
})
|
|
693
|
+
} catch (e) {
|
|
694
|
+
throw new CCIPError(
|
|
695
|
+
CCIPErrorCode.TRANSACTION_NOT_FINALIZED,
|
|
696
|
+
`Failed to send Sui executeReport transaction: ${(e as Error).message}`,
|
|
697
|
+
)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Check if transaction inmediately reverted
|
|
701
|
+
if (result.effects?.status?.status !== 'success') {
|
|
702
|
+
const errorMsg = result.effects?.status?.error || 'Unknown error'
|
|
703
|
+
throw new CCIPExecTxRevertedError(result.digest, {
|
|
704
|
+
context: { error: errorMsg },
|
|
705
|
+
})
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
this.logger.info(`Waiting for Sui transaction ${result.digest} to be finalized...`)
|
|
709
|
+
|
|
710
|
+
await this.client.waitForTransaction({
|
|
711
|
+
digest: result.digest,
|
|
712
|
+
options: {
|
|
713
|
+
showEffects: true,
|
|
714
|
+
showEvents: true,
|
|
715
|
+
},
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
// Return the transaction as a ChainTransaction
|
|
719
|
+
return this.getTransaction(result.digest)
|
|
272
720
|
}
|
|
273
721
|
|
|
274
722
|
/**
|
|
@@ -285,26 +733,26 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
285
733
|
|
|
286
734
|
/** {@inheritDoc Chain.getSupportedTokens} */
|
|
287
735
|
async getSupportedTokens(_address: string): Promise<string[]> {
|
|
288
|
-
return Promise.reject(new
|
|
736
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getSupportedTokens'))
|
|
289
737
|
}
|
|
290
738
|
|
|
291
739
|
/** {@inheritDoc Chain.getRegistryTokenConfig} */
|
|
292
740
|
async getRegistryTokenConfig(_address: string, _tokenName: string): Promise<never> {
|
|
293
|
-
return Promise.reject(new
|
|
741
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getRegistryTokenConfig'))
|
|
294
742
|
}
|
|
295
743
|
|
|
296
744
|
/** {@inheritDoc Chain.getTokenPoolConfigs} */
|
|
297
745
|
async getTokenPoolConfigs(_tokenPool: string): Promise<never> {
|
|
298
|
-
return Promise.reject(new
|
|
746
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getTokenPoolConfigs'))
|
|
299
747
|
}
|
|
300
748
|
|
|
301
749
|
/** {@inheritDoc Chain.getTokenPoolRemotes} */
|
|
302
750
|
async getTokenPoolRemotes(_tokenPool: string): Promise<never> {
|
|
303
|
-
return Promise.reject(new
|
|
751
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getTokenPoolRemotes'))
|
|
304
752
|
}
|
|
305
753
|
|
|
306
754
|
/** {@inheritDoc Chain.getFeeTokens} */
|
|
307
755
|
async getFeeTokens(_router: string): Promise<never> {
|
|
308
|
-
return Promise.reject(new
|
|
756
|
+
return Promise.reject(new CCIPNotImplementedError('SuiChain.getFeeTokens'))
|
|
309
757
|
}
|
|
310
758
|
}
|