@chainlink/ccip-sdk 0.94.0 → 0.96.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 +2 -2
- package/dist/all-chains.d.ts +23 -0
- package/dist/all-chains.d.ts.map +1 -0
- package/dist/all-chains.js +24 -0
- package/dist/all-chains.js.map +1 -0
- package/dist/api/index.d.ts +86 -7
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +270 -10
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +134 -13
- package/dist/api/types.d.ts.map +1 -1
- package/dist/aptos/index.d.ts +38 -17
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +91 -61
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.js +3 -3
- package/dist/aptos/logs.js.map +1 -1
- package/dist/chain.d.ts +300 -42
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +160 -9
- package/dist/chain.js.map +1 -1
- package/dist/errors/codes.d.ts +9 -3
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +10 -3
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +8 -8
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +8 -8
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +10 -4
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +62 -21
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +128 -41
- package/dist/errors/specialized.js.map +1 -1
- package/dist/evm/extra-args.d.ts +25 -0
- package/dist/evm/extra-args.d.ts.map +1 -0
- package/dist/evm/extra-args.js +328 -0
- package/dist/evm/extra-args.js.map +1 -0
- package/dist/evm/gas.d.ts +14 -0
- package/dist/evm/gas.d.ts.map +1 -0
- package/dist/evm/gas.js +92 -0
- package/dist/evm/gas.js.map +1 -0
- package/dist/evm/index.d.ts +76 -32
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +94 -104
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +8 -8
- package/dist/evm/offchain.js.map +1 -1
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +24 -3
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +103 -4
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +28 -3
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +46 -19
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +56 -68
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +18 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts +5 -4
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +7 -6
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +30 -20
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +86 -56
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts +2 -1
- package/dist/selectors.d.ts.map +1 -1
- package/dist/selectors.js +625 -278
- package/dist/selectors.js.map +1 -1
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +2 -1
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/index.d.ts +73 -22
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +91 -28
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.js +2 -2
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +6 -9
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/utils.d.ts +29 -1
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +39 -1
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/discovery.d.ts +7 -4
- package/dist/sui/discovery.d.ts.map +1 -1
- package/dist/sui/discovery.js +66 -19
- package/dist/sui/discovery.js.map +1 -1
- package/dist/sui/events.d.ts +23 -12
- package/dist/sui/events.d.ts.map +1 -1
- package/dist/sui/events.js +267 -128
- package/dist/sui/events.js.map +1 -1
- package/dist/sui/index.d.ts +57 -41
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +286 -159
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/objects.d.ts +14 -4
- package/dist/sui/objects.d.ts.map +1 -1
- package/dist/sui/objects.js +61 -68
- package/dist/sui/objects.js.map +1 -1
- package/dist/sui/types.d.ts +33 -0
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js.map +1 -1
- package/dist/ton/index.d.ts +67 -21
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +159 -30
- package/dist/ton/index.js.map +1 -1
- package/dist/ton/send.d.ts +52 -0
- package/dist/ton/send.d.ts.map +1 -0
- package/dist/ton/send.js +166 -0
- package/dist/ton/send.js.map +1 -0
- package/dist/ton/utils.d.ts +3 -3
- package/dist/ton/utils.d.ts.map +1 -1
- package/dist/ton/utils.js +6 -5
- package/dist/ton/utils.js.map +1 -1
- package/dist/types.d.ts +126 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +19 -5
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +67 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +126 -17
- package/dist/utils.js.map +1 -1
- package/package.json +14 -9
- package/src/all-chains.ts +26 -0
- package/src/api/index.ts +348 -13
- package/src/api/types.ts +160 -13
- package/src/aptos/index.ts +98 -76
- package/src/aptos/logs.ts +3 -3
- package/src/chain.ts +408 -51
- package/src/errors/codes.ts +10 -3
- package/src/errors/index.ts +8 -5
- package/src/errors/recovery.ts +18 -5
- package/src/errors/specialized.ts +168 -49
- package/src/evm/extra-args.ts +377 -0
- package/src/evm/gas.ts +150 -0
- package/src/evm/index.ts +123 -155
- package/src/evm/offchain.ts +15 -9
- package/src/execution.ts +26 -3
- package/src/extra-args.ts +108 -4
- package/src/gas.ts +101 -115
- package/src/index.ts +27 -14
- package/src/offchain.ts +12 -6
- package/src/requests.ts +117 -67
- package/src/selectors.ts +632 -280
- package/src/solana/exec.ts +3 -1
- package/src/solana/index.ts +97 -37
- package/src/solana/offchain.ts +2 -2
- package/src/solana/send.ts +5 -23
- package/src/solana/utils.ts +66 -0
- package/src/sui/discovery.ts +92 -31
- package/src/sui/events.ts +346 -239
- package/src/sui/index.ts +365 -212
- package/src/sui/objects.ts +74 -98
- package/src/sui/types.ts +35 -0
- package/src/ton/index.ts +199 -35
- package/src/ton/send.ts +222 -0
- package/src/ton/utils.ts +7 -6
- package/src/types.ts +128 -9
- package/src/utils.ts +169 -21
package/src/sui/objects.ts
CHANGED
|
@@ -5,9 +5,17 @@ import type { SuiClient } from '@mysten/sui/client'
|
|
|
5
5
|
import { Transaction } from '@mysten/sui/transactions'
|
|
6
6
|
import { normalizeSuiAddress } from '@mysten/sui/utils'
|
|
7
7
|
import { blake2b } from '@noble/hashes/blake2.js'
|
|
8
|
+
import { hexlify, toUtf8Bytes } from 'ethers'
|
|
9
|
+
import { memoize } from 'micro-memoize'
|
|
8
10
|
|
|
9
11
|
import { CCIPDataFormatUnsupportedError } from '../errors/index.ts'
|
|
10
12
|
import type { CCIPMessage, CCIPVersion } from '../types.ts'
|
|
13
|
+
import { toLeArray } from '../utils.ts'
|
|
14
|
+
|
|
15
|
+
const bcsBytes = (bytes: Uint8Array) => bcs.vector(bcs.u8()).serialize(bytes).toBytes()
|
|
16
|
+
|
|
17
|
+
const HASHING_INTENT_SCOPE_CHILD_OBJECT_ID = 0xf0
|
|
18
|
+
const SUI_FRAMEWORK_ADDRESS = '0x2'
|
|
11
19
|
|
|
12
20
|
/**
|
|
13
21
|
* Derive a dynamic field object ID using the Sui algorithm
|
|
@@ -19,27 +27,24 @@ export function deriveObjectID(parentAddress: string, keyBytes: Uint8Array): str
|
|
|
19
27
|
const parentBytes = bcs.Address.serialize(normalizedParent).toBytes()
|
|
20
28
|
|
|
21
29
|
// BCS serialize the key (vector<u8>)
|
|
22
|
-
const bcsKeyBytes =
|
|
30
|
+
const bcsKeyBytes = bcsBytes(keyBytes)
|
|
31
|
+
const keyLenBytes = toLeArray(bcsKeyBytes.length, 8) // uint64
|
|
23
32
|
|
|
24
33
|
// Construct TypeTag for DerivedObjectKey<vector<u8>>
|
|
25
|
-
const suiFrameworkAddress = bcs.Address.serialize(
|
|
34
|
+
const suiFrameworkAddress = bcs.Address.serialize(SUI_FRAMEWORK_ADDRESS).toBytes()
|
|
26
35
|
const typeTagBytes = new Uint8Array([
|
|
27
36
|
0x07, // TypeTag::Struct
|
|
28
37
|
...suiFrameworkAddress,
|
|
29
|
-
|
|
30
|
-
...
|
|
31
|
-
0x10, // struct name length
|
|
32
|
-
...new TextEncoder().encode('DerivedObjectKey'),
|
|
38
|
+
...bcsBytes(toUtf8Bytes('derived_object')), //module
|
|
39
|
+
...bcsBytes(toUtf8Bytes('DerivedObjectKey')), // struct name
|
|
33
40
|
0x01, // type params count
|
|
34
|
-
|
|
41
|
+
0x06, // TypeTag::Vector
|
|
42
|
+
0x01, // TypeTag::U8
|
|
35
43
|
])
|
|
36
44
|
|
|
37
45
|
// Build the hash input
|
|
38
|
-
const keyLenBytes = new Uint8Array(8)
|
|
39
|
-
new DataView(keyLenBytes.buffer).setBigUint64(0, BigInt(bcsKeyBytes.length), true)
|
|
40
|
-
|
|
41
46
|
const hashInput = new Uint8Array([
|
|
42
|
-
|
|
47
|
+
HASHING_INTENT_SCOPE_CHILD_OBJECT_ID,
|
|
43
48
|
...parentBytes,
|
|
44
49
|
...keyLenBytes,
|
|
45
50
|
...bcsKeyBytes,
|
|
@@ -49,101 +54,72 @@ export function deriveObjectID(parentAddress: string, keyBytes: Uint8Array): str
|
|
|
49
54
|
// Hash with Blake2b-256
|
|
50
55
|
const hash = blake2b(hashInput, { dkLen: 32 })
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
return normalizeSuiAddress('0x' + Buffer.from(hash).toString('hex'))
|
|
57
|
+
return hexlify(hash)
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/**
|
|
57
|
-
*
|
|
61
|
+
* Finds the StatePointer object owned by a package.
|
|
62
|
+
* The StatePointer contains a reference to the parent object used for derivation.
|
|
58
63
|
*/
|
|
59
|
-
export
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Get the pointer object to extract ccip_object_id
|
|
75
|
-
const pointerId = pointerResponse.data[0]!.data?.objectId
|
|
76
|
-
if (!pointerId) {
|
|
77
|
-
throw new CCIPDataFormatUnsupportedError('Pointer does not have objectId')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const pointerObject = await client.getObject({
|
|
81
|
-
id: pointerId,
|
|
82
|
-
options: { showContent: true },
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
if (pointerObject.data?.content?.dataType !== 'moveObject') {
|
|
86
|
-
throw new CCIPDataFormatUnsupportedError('Pointer object is not a Move object')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const ccipObjectId = (pointerObject.data.content.fields as Record<string, unknown>)[
|
|
90
|
-
'ccip_object_id'
|
|
91
|
-
] as string
|
|
92
|
-
|
|
93
|
-
if (!ccipObjectId) {
|
|
94
|
-
throw new CCIPDataFormatUnsupportedError('Could not find ccip_object_id in pointer')
|
|
95
|
-
}
|
|
64
|
+
export const getObjectRef = memoize(
|
|
65
|
+
async function getPackageIds_(address: string, client: SuiClient): Promise<string> {
|
|
66
|
+
let stateObjectName
|
|
67
|
+
if (address.endsWith('::onramp')) stateObjectName = 'OnRampState'
|
|
68
|
+
else if (address.endsWith('::offramp')) stateObjectName = 'OffRampState'
|
|
69
|
+
else stateObjectName = 'CCIPObjectRef'
|
|
70
|
+
|
|
71
|
+
const fullStatePointerType = `${address}::${stateObjectName}Pointer`
|
|
72
|
+
|
|
73
|
+
const ownedObjects = await client.getOwnedObjects({
|
|
74
|
+
owner: address.split('::')[0]!,
|
|
75
|
+
filter: { StructType: fullStatePointerType },
|
|
76
|
+
options: { showContent: true },
|
|
77
|
+
})
|
|
96
78
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
79
|
+
const pointer = ownedObjects.data[0]?.data
|
|
80
|
+
if (!pointer?.objectId || pointer.content!.dataType !== 'moveObject')
|
|
81
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
82
|
+
'No CCIP ObjectRef Pointer found for the given packageId',
|
|
83
|
+
{ context: { fullStatePointerType, pointer } },
|
|
84
|
+
)
|
|
85
|
+
// const statePointerObjectId = pointer.objectId
|
|
86
|
+
const parentObjectId = Object.entries(pointer.content!.fields).find(([key]) =>
|
|
87
|
+
key.endsWith('_object_id'),
|
|
88
|
+
)?.[1]
|
|
89
|
+
if (typeof parentObjectId !== 'string')
|
|
90
|
+
throw new CCIPDataFormatUnsupportedError('No parent object id found inthe given pointer', {
|
|
91
|
+
context: { fullStatePointerType, pointer },
|
|
92
|
+
})
|
|
93
|
+
return deriveObjectID(parentObjectId, toUtf8Bytes(stateObjectName))
|
|
94
|
+
},
|
|
95
|
+
{ maxArgs: 1, expires: 300e3, async: true },
|
|
96
|
+
)
|
|
100
97
|
|
|
101
98
|
/**
|
|
102
|
-
*
|
|
99
|
+
* Finds the StatePointer object owned by a package.
|
|
100
|
+
* The StatePointer contains a reference to the parent object used for derivation.
|
|
103
101
|
*/
|
|
104
|
-
export
|
|
105
|
-
client: SuiClient
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const offrampPointerObject = await client.getObject({
|
|
128
|
-
id: offrampPointerId,
|
|
129
|
-
options: { showContent: true },
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
if (offrampPointerObject.data?.content?.dataType !== 'moveObject') {
|
|
133
|
-
throw new CCIPDataFormatUnsupportedError('OffRamp pointer object is not a Move object')
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const offrampObjectId = (offrampPointerObject.data.content.fields as Record<string, unknown>)[
|
|
137
|
-
'off_ramp_object_id'
|
|
138
|
-
] as string
|
|
139
|
-
|
|
140
|
-
if (!offrampObjectId) {
|
|
141
|
-
throw new CCIPDataFormatUnsupportedError('Could not find off_ramp_object_id in pointer')
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Derive the OffRampState ID from the parent OffRamp Object ID
|
|
145
|
-
return deriveObjectID(offrampObjectId, new TextEncoder().encode('OffRampState'))
|
|
146
|
-
}
|
|
102
|
+
export const getLatestPackageId = memoize(
|
|
103
|
+
async function getLatestPackageId_(address: string, client: SuiClient): Promise<string> {
|
|
104
|
+
const suffix = address.split('::')[1]
|
|
105
|
+
try {
|
|
106
|
+
const stateObjectId = await getObjectRef(address, client)
|
|
107
|
+
const stateObject = await client.getObject({
|
|
108
|
+
id: stateObjectId,
|
|
109
|
+
options: { showContent: true },
|
|
110
|
+
})
|
|
111
|
+
const stateContent = stateObject.data?.content
|
|
112
|
+
if (stateContent?.dataType !== 'moveObject') return address
|
|
113
|
+
const packageIdsField = (stateContent.fields as Record<string, unknown>)['package_ids']
|
|
114
|
+
if (!Array.isArray(packageIdsField) || packageIdsField.length === 0) return address
|
|
115
|
+
const latest = packageIdsField[packageIdsField.length - 1] as string
|
|
116
|
+
return suffix ? `${latest}::${suffix}` : latest
|
|
117
|
+
} catch {
|
|
118
|
+
return address
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{ maxArgs: 1, expires: 60e3, async: true },
|
|
122
|
+
)
|
|
147
123
|
|
|
148
124
|
/**
|
|
149
125
|
* Get the receiver module configuration from the receiver registry.
|
package/src/sui/types.ts
CHANGED
|
@@ -15,6 +15,16 @@ export const SuiExtraArgsV1Codec = bcs.struct('SuiExtraArgsV1', {
|
|
|
15
15
|
receiverObjectIds: bcs.vector(bcs.vector(bcs.u8())),
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
+
/** Token amount data structure for Sui CCIP messages. */
|
|
19
|
+
export type SuiTokenAmount = {
|
|
20
|
+
source_pool_address?: string
|
|
21
|
+
dest_token_address?: number[]
|
|
22
|
+
extra_data?: number[]
|
|
23
|
+
amount?: string | number
|
|
24
|
+
dest_exec_data?: number[]
|
|
25
|
+
dest_gas_amount?: string | number
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
/**
|
|
19
29
|
* Encodes Sui v1 extra arguments using BCS encoding.
|
|
20
30
|
* @param args - Sui extra arguments to encode.
|
|
@@ -26,3 +36,28 @@ export function encodeSuiExtraArgsV1(args: SuiExtraArgsV1): string {
|
|
|
26
36
|
const bcsData = SuiExtraArgsV1Codec.serialize({ ...args, tokenReceiver, receiverObjectIds })
|
|
27
37
|
return concat([SuiExtraArgsV1Tag, bcsData.toBytes()])
|
|
28
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sui-specific CCIP message log structure from events.
|
|
42
|
+
*/
|
|
43
|
+
export type SuiCCIPMessageLog = {
|
|
44
|
+
dest_chain_selector: string
|
|
45
|
+
message: {
|
|
46
|
+
data: number[]
|
|
47
|
+
extra_args: number[]
|
|
48
|
+
fee_token: string
|
|
49
|
+
fee_token_amount: string
|
|
50
|
+
fee_value_juels: string
|
|
51
|
+
header: {
|
|
52
|
+
dest_chain_selector: string
|
|
53
|
+
message_id: number[]
|
|
54
|
+
nonce: string
|
|
55
|
+
sequence_number: string
|
|
56
|
+
source_chain_selector: string
|
|
57
|
+
}
|
|
58
|
+
receiver: number[]
|
|
59
|
+
sender: string
|
|
60
|
+
token_amounts: SuiTokenAmount[]
|
|
61
|
+
}
|
|
62
|
+
sequence_number: string
|
|
63
|
+
}
|
package/src/ton/index.ts
CHANGED
|
@@ -8,7 +8,8 @@ import { type Memoized, memoize } from 'micro-memoize'
|
|
|
8
8
|
import type { PickDeep } from 'type-fest'
|
|
9
9
|
|
|
10
10
|
import { streamTransactionsForAddress } from './logs.ts'
|
|
11
|
-
import {
|
|
11
|
+
import { generateUnsignedCcipSend, getFee as getFeeImpl } from './send.ts'
|
|
12
|
+
import { type ChainContext, type GetBalanceOpts, type LogFilter, Chain } from '../chain.ts'
|
|
12
13
|
import {
|
|
13
14
|
CCIPArgumentInvalidError,
|
|
14
15
|
CCIPExtraArgsInvalidError,
|
|
@@ -21,7 +22,8 @@ import {
|
|
|
21
22
|
CCIPWalletInvalidError,
|
|
22
23
|
} from '../errors/specialized.ts'
|
|
23
24
|
import { type EVMExtraArgsV2, type ExtraArgs, EVMExtraArgsV2Tag } from '../extra-args.ts'
|
|
24
|
-
import {
|
|
25
|
+
import type { LeafHasher } from '../hasher/common.ts'
|
|
26
|
+
import { buildMessageForDest } from '../requests.ts'
|
|
25
27
|
import { supportedChains } from '../supported-chains.ts'
|
|
26
28
|
import {
|
|
27
29
|
type CCIPExecution,
|
|
@@ -35,6 +37,7 @@ import {
|
|
|
35
37
|
type NetworkInfo,
|
|
36
38
|
type OffchainTokenData,
|
|
37
39
|
type WithLogger,
|
|
40
|
+
CCIPVersion,
|
|
38
41
|
ChainFamily,
|
|
39
42
|
ExecutionState,
|
|
40
43
|
} from '../types.ts'
|
|
@@ -50,7 +53,6 @@ import { generateUnsignedExecuteReport as generateUnsignedExecuteReportImpl } fr
|
|
|
50
53
|
import { getTONLeafHasher } from './hasher.ts'
|
|
51
54
|
import { type CCIPMessage_V1_6_TON, type UnsignedTONTx, isTONWallet } from './types.ts'
|
|
52
55
|
import { crc32, lookupTxByRawHash, parseJettonContent } from './utils.ts'
|
|
53
|
-
import type { LeafHasher } from '../hasher/common.ts'
|
|
54
56
|
export type { TONWallet, UnsignedTONTx } from './types.ts'
|
|
55
57
|
|
|
56
58
|
/**
|
|
@@ -164,13 +166,13 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
164
166
|
ctx?: ChainContext & { fetchFn?: typeof fetch },
|
|
165
167
|
): Promise<TONChain> {
|
|
166
168
|
// Verify connection by getting the latest block
|
|
167
|
-
const
|
|
169
|
+
const isMainnet =
|
|
168
170
|
(
|
|
169
171
|
await client.getContractState(
|
|
170
172
|
Address.parse('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'), // mainnet USDT
|
|
171
173
|
)
|
|
172
|
-
).state
|
|
173
|
-
return new TONChain(client, networkInfo(
|
|
174
|
+
).state === 'active'
|
|
175
|
+
return new TONChain(client, networkInfo(isMainnet ? 'ton-mainnet' : 'ton-testnet'), ctx)
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
/**
|
|
@@ -180,6 +182,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
180
182
|
* @param url - RPC endpoint URL for TonClient (v2).
|
|
181
183
|
* @param ctx - Context containing logger.
|
|
182
184
|
* @returns A new TONChain instance.
|
|
185
|
+
* @throws {@link CCIPHttpError} if connection to the RPC endpoint fails
|
|
183
186
|
*/
|
|
184
187
|
static async fromUrl(url: string, ctx?: ChainContext): Promise<TONChain> {
|
|
185
188
|
const { logger = console } = ctx ?? {}
|
|
@@ -220,6 +223,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
220
223
|
*
|
|
221
224
|
* @param block - Logical time (lt) as number, or 'finalized' for latest block timestamp
|
|
222
225
|
* @returns Unix timestamp in seconds
|
|
226
|
+
* @throws {@link CCIPNotImplementedError} if lt is not in cache
|
|
223
227
|
*/
|
|
224
228
|
async getBlockTimestamp(block: number | 'finalized'): Promise<number> {
|
|
225
229
|
if (typeof block != 'number') {
|
|
@@ -247,6 +251,8 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
247
251
|
* @param tx - Transaction identifier in either format
|
|
248
252
|
* @returns ChainTransaction with transaction details
|
|
249
253
|
* Note: `blockNumber` contains logical time (lt), not block seqno
|
|
254
|
+
* @throws {@link CCIPArgumentInvalidError} if hash format is invalid
|
|
255
|
+
* @throws {@link CCIPTransactionNotFoundError} if transaction not found
|
|
250
256
|
*/
|
|
251
257
|
async getTransaction(tx: string | Transaction): Promise<ChainTransaction> {
|
|
252
258
|
let address
|
|
@@ -264,7 +270,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
264
270
|
)
|
|
265
271
|
const txInfo = await lookupTxByRawHash(
|
|
266
272
|
cleanHash,
|
|
267
|
-
this.network.
|
|
273
|
+
this.network.networkType,
|
|
268
274
|
this.rateLimitedFetch,
|
|
269
275
|
this,
|
|
270
276
|
)
|
|
@@ -341,6 +347,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
341
347
|
* not block sequence numbers. This is because TON transaction APIs are indexed by lt.
|
|
342
348
|
*
|
|
343
349
|
* @param opts - Log filter options (startBlock/endBlock are interpreted as lt values)
|
|
350
|
+
* @throws {@link CCIPTopicsInvalidError} if topics format is invalid
|
|
344
351
|
*/
|
|
345
352
|
async *getLogs(opts: LogFilter): AsyncIterableIterator<Log_> {
|
|
346
353
|
let topics
|
|
@@ -363,12 +370,10 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
363
370
|
}
|
|
364
371
|
}
|
|
365
372
|
|
|
366
|
-
/**
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
/** {@inheritDoc Chain.getMessagesInBatch} */
|
|
373
|
+
/**
|
|
374
|
+
* {@inheritDoc Chain.getMessagesInBatch}
|
|
375
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
376
|
+
*/
|
|
372
377
|
override async getMessagesInBatch<
|
|
373
378
|
R extends PickDeep<
|
|
374
379
|
CCIPRequest,
|
|
@@ -426,9 +431,14 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
426
431
|
return stack.readAddress().toRawString()
|
|
427
432
|
}
|
|
428
433
|
|
|
429
|
-
/**
|
|
434
|
+
/**
|
|
435
|
+
* {@inheritDoc Chain.getNativeTokenForRouter}
|
|
436
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
437
|
+
*/
|
|
430
438
|
getNativeTokenForRouter(_router: string): Promise<string> {
|
|
431
|
-
|
|
439
|
+
// TON native token is represented as address 0:0...01 (workchain 0, hash = 1)
|
|
440
|
+
// This is a convention for representing native TON in CCIP
|
|
441
|
+
return Promise.resolve('0:0000000000000000000000000000000000000000000000000000000000000001')
|
|
432
442
|
}
|
|
433
443
|
|
|
434
444
|
/** {@inheritDoc Chain.getOffRampsForRouter} */
|
|
@@ -451,7 +461,10 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
451
461
|
return stack.readAddress().toRawString()
|
|
452
462
|
}
|
|
453
463
|
|
|
454
|
-
/**
|
|
464
|
+
/**
|
|
465
|
+
* {@inheritDoc Chain.getOnRampForOffRamp}
|
|
466
|
+
* @throws {@link CCIPSourceChainUnsupportedError} if source chain is not configured
|
|
467
|
+
*/
|
|
455
468
|
async getOnRampForOffRamp(offRamp: string, sourceChainSelector: bigint): Promise<string> {
|
|
456
469
|
try {
|
|
457
470
|
const offRampContract = this.provider.provider(Address.parse(offRamp))
|
|
@@ -497,7 +510,10 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
497
510
|
return Promise.resolve(offRamp)
|
|
498
511
|
}
|
|
499
512
|
|
|
500
|
-
/**
|
|
513
|
+
/**
|
|
514
|
+
* {@inheritDoc Chain.getTokenForTokenPool}
|
|
515
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
516
|
+
*/
|
|
501
517
|
async getTokenForTokenPool(_tokenPool: string): Promise<string> {
|
|
502
518
|
return Promise.reject(new CCIPNotImplementedError('getTokenForTokenPool'))
|
|
503
519
|
}
|
|
@@ -525,7 +541,45 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
525
541
|
}
|
|
526
542
|
}
|
|
527
543
|
|
|
528
|
-
/**
|
|
544
|
+
/**
|
|
545
|
+
* {@inheritDoc Chain.getBalance}
|
|
546
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
547
|
+
*/
|
|
548
|
+
async getBalance(opts: GetBalanceOpts): Promise<bigint> {
|
|
549
|
+
const { holder, token } = opts
|
|
550
|
+
const holderAddress = Address.parse(holder)
|
|
551
|
+
|
|
552
|
+
if (!token) {
|
|
553
|
+
// Get native TON balance
|
|
554
|
+
const state = await this.provider.getContractState(holderAddress)
|
|
555
|
+
return state.balance
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// For jetton balance, we need to:
|
|
559
|
+
// 1. Derive the jetton wallet address for this holder
|
|
560
|
+
// 2. Query the balance from that wallet contract
|
|
561
|
+
const jettonMaster = Address.parse(token)
|
|
562
|
+
const { stack } = await this.provider.runMethod(jettonMaster, 'get_wallet_address', [
|
|
563
|
+
{ type: 'slice', cell: beginCell().storeAddress(holderAddress).endCell() },
|
|
564
|
+
])
|
|
565
|
+
const jettonWalletAddress = stack.readAddress()
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
const { stack: balanceStack } = await this.provider.runMethod(
|
|
569
|
+
jettonWalletAddress,
|
|
570
|
+
'get_wallet_data',
|
|
571
|
+
)
|
|
572
|
+
return balanceStack.readBigNumber() // First value is balance
|
|
573
|
+
} catch {
|
|
574
|
+
// Wallet doesn't exist yet = 0 balance
|
|
575
|
+
return 0n
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* {@inheritDoc Chain.getTokenAdminRegistryFor}
|
|
581
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
582
|
+
*/
|
|
529
583
|
getTokenAdminRegistryFor(_address: string): Promise<string> {
|
|
530
584
|
return Promise.reject(new CCIPNotImplementedError('getTokenAdminRegistryFor'))
|
|
531
585
|
}
|
|
@@ -833,6 +887,7 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
833
887
|
* and raw format strings ("workchain:hash").
|
|
834
888
|
* @param bytes - Bytes or string to convert.
|
|
835
889
|
* @returns TON raw address string in format "workchain:hash".
|
|
890
|
+
* @throws {@link CCIPArgumentInvalidError} if bytes length is invalid
|
|
836
891
|
*/
|
|
837
892
|
static getAddress(bytes: BytesLike): string {
|
|
838
893
|
// If it's already a string address, try to parse and return raw format
|
|
@@ -952,20 +1007,107 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
952
1007
|
}
|
|
953
1008
|
|
|
954
1009
|
/** {@inheritDoc Chain.getFee} */
|
|
955
|
-
async getFee(
|
|
956
|
-
|
|
1010
|
+
async getFee({
|
|
1011
|
+
router,
|
|
1012
|
+
destChainSelector,
|
|
1013
|
+
message,
|
|
1014
|
+
}: Parameters<Chain['getFee']>[0]): Promise<bigint> {
|
|
1015
|
+
return getFeeImpl(
|
|
1016
|
+
this,
|
|
1017
|
+
router,
|
|
1018
|
+
destChainSelector,
|
|
1019
|
+
buildMessageForDest(message, networkInfo(destChainSelector).family),
|
|
1020
|
+
)
|
|
957
1021
|
}
|
|
958
1022
|
|
|
959
1023
|
/** {@inheritDoc Chain.generateUnsignedSendMessage} */
|
|
960
|
-
generateUnsignedSendMessage(
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1024
|
+
async generateUnsignedSendMessage({
|
|
1025
|
+
router,
|
|
1026
|
+
destChainSelector,
|
|
1027
|
+
message,
|
|
1028
|
+
sender,
|
|
1029
|
+
}: Parameters<Chain['generateUnsignedSendMessage']>[0]): Promise<UnsignedTONTx> {
|
|
1030
|
+
// Convert MessageInput to AnyMessage with defaults
|
|
1031
|
+
const populatedMessage = buildMessageForDest(message, networkInfo(destChainSelector).family)
|
|
1032
|
+
|
|
1033
|
+
// Calculate fee if not provided
|
|
1034
|
+
const fee =
|
|
1035
|
+
message.fee ??
|
|
1036
|
+
(await this.getFee({
|
|
1037
|
+
router,
|
|
1038
|
+
destChainSelector,
|
|
1039
|
+
message: populatedMessage,
|
|
1040
|
+
}))
|
|
1041
|
+
|
|
1042
|
+
const unsigned = generateUnsignedCcipSend(this, sender, router, destChainSelector, {
|
|
1043
|
+
...populatedMessage,
|
|
1044
|
+
fee,
|
|
1045
|
+
})
|
|
1046
|
+
|
|
1047
|
+
return {
|
|
1048
|
+
family: ChainFamily.TON,
|
|
1049
|
+
...unsigned,
|
|
1050
|
+
}
|
|
964
1051
|
}
|
|
965
1052
|
|
|
966
1053
|
/** {@inheritDoc Chain.sendMessage} */
|
|
967
|
-
async sendMessage(
|
|
968
|
-
|
|
1054
|
+
async sendMessage({
|
|
1055
|
+
router,
|
|
1056
|
+
destChainSelector,
|
|
1057
|
+
message,
|
|
1058
|
+
wallet,
|
|
1059
|
+
}: Parameters<Chain['sendMessage']>[0]): Promise<CCIPRequest> {
|
|
1060
|
+
if (!isTONWallet(wallet)) {
|
|
1061
|
+
throw new CCIPWalletInvalidError(wallet)
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const sender = await wallet.getAddress()
|
|
1065
|
+
|
|
1066
|
+
// Generate unsigned transaction with fee calculation if needed
|
|
1067
|
+
const { family: _, ...unsigned } = await this.generateUnsignedSendMessage({
|
|
1068
|
+
router,
|
|
1069
|
+
destChainSelector,
|
|
1070
|
+
message,
|
|
1071
|
+
sender,
|
|
1072
|
+
})
|
|
1073
|
+
|
|
1074
|
+
// Send transaction
|
|
1075
|
+
const startTime = Math.floor(Date.now() / 1000)
|
|
1076
|
+
const seqno = await wallet.sendTransaction(unsigned)
|
|
1077
|
+
|
|
1078
|
+
this.logger.info('CCIP send transaction submitted, seqno:', seqno)
|
|
1079
|
+
|
|
1080
|
+
// Wait for CCIPMessageSent event and extract the request
|
|
1081
|
+
// Query the OnRamp for the CCIPMessageSent event
|
|
1082
|
+
const onRamp = await this.getOnRampForRouter(router, destChainSelector)
|
|
1083
|
+
|
|
1084
|
+
// Poll for the message in recent logs
|
|
1085
|
+
for await (const log of this.getLogs({
|
|
1086
|
+
address: onRamp,
|
|
1087
|
+
topics: [crc32('CCIPMessageSent')],
|
|
1088
|
+
startTime,
|
|
1089
|
+
watch: sleep(5 * 60e3 /* 5m timeout */),
|
|
1090
|
+
})) {
|
|
1091
|
+
const msg = TONChain.decodeMessage(log)
|
|
1092
|
+
if (!msg) continue
|
|
1093
|
+
|
|
1094
|
+
// Found our message: construct and return the CCIPRequest
|
|
1095
|
+
const tx = log.tx ?? (await this.getTransaction(log.transactionHash))
|
|
1096
|
+
|
|
1097
|
+
return {
|
|
1098
|
+
lane: {
|
|
1099
|
+
sourceChainSelector: this.network.chainSelector,
|
|
1100
|
+
destChainSelector,
|
|
1101
|
+
onRamp,
|
|
1102
|
+
version: CCIPVersion.V1_6,
|
|
1103
|
+
},
|
|
1104
|
+
message: msg,
|
|
1105
|
+
log,
|
|
1106
|
+
tx,
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
throw new CCIPTransactionNotFoundError(seqno.toString())
|
|
969
1111
|
}
|
|
970
1112
|
|
|
971
1113
|
/** {@inheritDoc Chain.getOffchainTokenData} */
|
|
@@ -973,7 +1115,10 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
973
1115
|
return Promise.resolve(request.message.tokenAmounts.map(() => undefined))
|
|
974
1116
|
}
|
|
975
1117
|
|
|
976
|
-
/**
|
|
1118
|
+
/**
|
|
1119
|
+
* {@inheritDoc Chain.generateUnsignedExecuteReport}
|
|
1120
|
+
* @throws {@link CCIPExtraArgsInvalidError} if extra args are not EVMExtraArgsV2 format
|
|
1121
|
+
*/
|
|
977
1122
|
generateUnsignedExecuteReport({
|
|
978
1123
|
offRamp,
|
|
979
1124
|
execReport,
|
|
@@ -995,7 +1140,11 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
995
1140
|
})
|
|
996
1141
|
}
|
|
997
1142
|
|
|
998
|
-
/**
|
|
1143
|
+
/**
|
|
1144
|
+
* {@inheritDoc Chain.executeReport}
|
|
1145
|
+
* @throws {@link CCIPWalletInvalidError} if wallet is not a valid TON wallet
|
|
1146
|
+
* @throws {@link CCIPReceiptNotFoundError} if execution receipt not found within timeout
|
|
1147
|
+
*/
|
|
999
1148
|
async executeReport(opts: Parameters<Chain['executeReport']>[0]): Promise<CCIPExecution> {
|
|
1000
1149
|
const { offRamp, wallet } = opts
|
|
1001
1150
|
if (!isTONWallet(wallet)) {
|
|
@@ -1040,27 +1189,42 @@ export class TONChain extends Chain<typeof ChainFamily.TON> {
|
|
|
1040
1189
|
}
|
|
1041
1190
|
}
|
|
1042
1191
|
|
|
1043
|
-
/**
|
|
1192
|
+
/**
|
|
1193
|
+
* {@inheritDoc Chain.getSupportedTokens}
|
|
1194
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
1195
|
+
*/
|
|
1044
1196
|
async getSupportedTokens(_address: string): Promise<string[]> {
|
|
1045
1197
|
return Promise.reject(new CCIPNotImplementedError('getSupportedTokens'))
|
|
1046
1198
|
}
|
|
1047
1199
|
|
|
1048
|
-
/**
|
|
1200
|
+
/**
|
|
1201
|
+
* {@inheritDoc Chain.getRegistryTokenConfig}
|
|
1202
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
1203
|
+
*/
|
|
1049
1204
|
async getRegistryTokenConfig(_address: string, _tokenName: string): Promise<never> {
|
|
1050
1205
|
return Promise.reject(new CCIPNotImplementedError('getRegistryTokenConfig'))
|
|
1051
1206
|
}
|
|
1052
1207
|
|
|
1053
|
-
/**
|
|
1054
|
-
|
|
1055
|
-
|
|
1208
|
+
/**
|
|
1209
|
+
* {@inheritDoc Chain.getTokenPoolConfig}
|
|
1210
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
1211
|
+
*/
|
|
1212
|
+
async getTokenPoolConfig(_tokenPool: string): Promise<never> {
|
|
1213
|
+
return Promise.reject(new CCIPNotImplementedError('getTokenPoolConfig'))
|
|
1056
1214
|
}
|
|
1057
1215
|
|
|
1058
|
-
/**
|
|
1216
|
+
/**
|
|
1217
|
+
* {@inheritDoc Chain.getTokenPoolRemotes}
|
|
1218
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
1219
|
+
*/
|
|
1059
1220
|
async getTokenPoolRemotes(_tokenPool: string): Promise<never> {
|
|
1060
1221
|
return Promise.reject(new CCIPNotImplementedError('getTokenPoolRemotes'))
|
|
1061
1222
|
}
|
|
1062
1223
|
|
|
1063
|
-
/**
|
|
1224
|
+
/**
|
|
1225
|
+
* {@inheritDoc Chain.getFeeTokens}
|
|
1226
|
+
* @throws {@link CCIPNotImplementedError} always (not implemented for TON)
|
|
1227
|
+
*/
|
|
1064
1228
|
async getFeeTokens(_router: string): Promise<never> {
|
|
1065
1229
|
return Promise.reject(new CCIPNotImplementedError('getFeeTokens'))
|
|
1066
1230
|
}
|