@chainlink/ccip-sdk 0.91.1 → 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,155 @@
|
|
|
1
|
+
import { type Connection, PublicKey } from '@solana/web3.js'
|
|
2
|
+
|
|
3
|
+
import type { LogFilter } from '../chain.ts'
|
|
4
|
+
import type { SolanaTransaction } from './index.ts'
|
|
5
|
+
import {
|
|
6
|
+
CCIPLogsAddressRequiredError,
|
|
7
|
+
CCIPLogsWatchRequiresFinalityError,
|
|
8
|
+
CCIPLogsWatchRequiresStartError,
|
|
9
|
+
} from '../errors/index.ts'
|
|
10
|
+
import { sleep } from '../utils.ts'
|
|
11
|
+
|
|
12
|
+
const DEFAULT_POLL_INTERVAL = 5e3
|
|
13
|
+
|
|
14
|
+
async function* fetchSigsForward(
|
|
15
|
+
opts: LogFilter & { pollInterval?: number },
|
|
16
|
+
ctx: { connection: Connection },
|
|
17
|
+
) {
|
|
18
|
+
const { connection } = ctx
|
|
19
|
+
const limit = Math.min(opts?.page || 1000, 1000)
|
|
20
|
+
const commitment = opts.endBlock === 'finalized' ? 'finalized' : 'confirmed'
|
|
21
|
+
|
|
22
|
+
// forward collect all matching sigs in array
|
|
23
|
+
const allSigs = [] as Awaited<ReturnType<typeof connection.getSignaturesForAddress>>
|
|
24
|
+
let batch: typeof allSigs, until: string
|
|
25
|
+
do {
|
|
26
|
+
batch = await connection.getSignaturesForAddress(
|
|
27
|
+
new PublicKey(opts.address!),
|
|
28
|
+
{ limit, before: allSigs[allSigs.length - 1]?.signature ?? opts.endBefore },
|
|
29
|
+
commitment,
|
|
30
|
+
)
|
|
31
|
+
until ??= batch[0]?.signature
|
|
32
|
+
|
|
33
|
+
while (
|
|
34
|
+
batch.length > 0 &&
|
|
35
|
+
(batch[batch.length - 1].slot < (opts.startBlock || 0) ||
|
|
36
|
+
(batch[batch.length - 1].blockTime || -1) < (opts.startTime || 0))
|
|
37
|
+
) {
|
|
38
|
+
batch.length-- // truncate tail of txs which are older than requested start
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
allSigs.push(...batch) // concat in descending order
|
|
42
|
+
} while (batch.length >= limit)
|
|
43
|
+
|
|
44
|
+
allSigs.reverse() // forward
|
|
45
|
+
|
|
46
|
+
const notAfter =
|
|
47
|
+
typeof opts.endBlock !== 'number'
|
|
48
|
+
? undefined
|
|
49
|
+
: opts.endBlock < 0
|
|
50
|
+
? (await connection.getSlot('confirmed')) + opts.endBlock
|
|
51
|
+
: opts.endBlock
|
|
52
|
+
while (notAfter && allSigs.length > 0 && allSigs[allSigs.length - 1].slot > notAfter) {
|
|
53
|
+
allSigs.length-- // truncate head (after reverse) of txs newer than requested end
|
|
54
|
+
}
|
|
55
|
+
yield* allSigs // all past logs
|
|
56
|
+
|
|
57
|
+
if (allSigs.length) until = allSigs[allSigs.length - 1].signature
|
|
58
|
+
let lastReq = performance.now()
|
|
59
|
+
// if not watch mode, returns
|
|
60
|
+
while (opts.watch) {
|
|
61
|
+
let break$ = sleep(
|
|
62
|
+
Math.max((opts.pollInterval || DEFAULT_POLL_INTERVAL) - (performance.now() - lastReq), 1),
|
|
63
|
+
).then(() => false)
|
|
64
|
+
if (opts.watch instanceof Promise) break$ = Promise.race([break$, opts.watch.then(() => true)])
|
|
65
|
+
if (await break$) break
|
|
66
|
+
|
|
67
|
+
lastReq = performance.now()
|
|
68
|
+
batch = await connection.getSignaturesForAddress(
|
|
69
|
+
new PublicKey(opts.address!),
|
|
70
|
+
{ limit, until },
|
|
71
|
+
commitment,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
batch.reverse() // forward
|
|
75
|
+
|
|
76
|
+
const notAfter =
|
|
77
|
+
batch.length === 0 || typeof opts.endBlock !== 'number'
|
|
78
|
+
? undefined
|
|
79
|
+
: opts.endBlock < 0
|
|
80
|
+
? (await connection.getSlot('confirmed')) + opts.endBlock
|
|
81
|
+
: opts.endBlock
|
|
82
|
+
|
|
83
|
+
for (const sig of batch) {
|
|
84
|
+
if (notAfter && sig.slot > notAfter) break
|
|
85
|
+
until = sig.signature
|
|
86
|
+
yield sig
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function* fetchSigsBackwards(
|
|
92
|
+
opts: LogFilter & { pollInterval?: number },
|
|
93
|
+
ctx: { connection: Connection },
|
|
94
|
+
) {
|
|
95
|
+
const { connection } = ctx
|
|
96
|
+
const limit = Math.min(opts?.page || 1000, 1000)
|
|
97
|
+
const commitment = opts.endBlock === 'finalized' ? 'finalized' : 'confirmed'
|
|
98
|
+
|
|
99
|
+
if (typeof opts.endBlock === 'number' && opts.endBlock < 0)
|
|
100
|
+
opts.endBlock = (await connection.getSlot('confirmed')) + opts.endBlock
|
|
101
|
+
|
|
102
|
+
let batch: Awaited<ReturnType<typeof connection.getSignaturesForAddress>> | undefined
|
|
103
|
+
do {
|
|
104
|
+
batch = await connection.getSignaturesForAddress(
|
|
105
|
+
new PublicKey(opts.address!),
|
|
106
|
+
{
|
|
107
|
+
limit,
|
|
108
|
+
before: batch?.length
|
|
109
|
+
? batch[batch.length - 1].signature
|
|
110
|
+
: opts.endBefore
|
|
111
|
+
? opts.endBefore
|
|
112
|
+
: undefined,
|
|
113
|
+
},
|
|
114
|
+
commitment,
|
|
115
|
+
)
|
|
116
|
+
for (const sig of batch) {
|
|
117
|
+
if (typeof opts.endBlock === 'number' && sig.slot > opts.endBlock) continue
|
|
118
|
+
yield sig
|
|
119
|
+
}
|
|
120
|
+
} while (batch.length >= limit)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Internal method to get transactions for an address with pagination.
|
|
125
|
+
* @param opts - Log filter options.
|
|
126
|
+
* @returns Async generator of Solana transactions.
|
|
127
|
+
*/
|
|
128
|
+
export async function* getTransactionsForAddress(
|
|
129
|
+
opts: Omit<LogFilter, 'topics'> & { pollInterval?: number },
|
|
130
|
+
ctx: {
|
|
131
|
+
connection: Connection
|
|
132
|
+
getTransaction: (signature: string) => Promise<SolanaTransaction>
|
|
133
|
+
},
|
|
134
|
+
): AsyncGenerator<SolanaTransaction> {
|
|
135
|
+
if (!opts.address) throw new CCIPLogsAddressRequiredError()
|
|
136
|
+
|
|
137
|
+
opts.endBlock ||= 'latest'
|
|
138
|
+
|
|
139
|
+
let allSignatures
|
|
140
|
+
if (opts.startBlock != null || opts.startTime != null) {
|
|
141
|
+
if (opts.watch && ((typeof opts.endBlock === 'number' && opts.endBlock > 0) || opts.endBefore))
|
|
142
|
+
throw new CCIPLogsWatchRequiresFinalityError(opts.endBlock)
|
|
143
|
+
|
|
144
|
+
allSignatures = fetchSigsForward(opts, ctx)
|
|
145
|
+
} else {
|
|
146
|
+
if (opts.watch) throw new CCIPLogsWatchRequiresStartError()
|
|
147
|
+
|
|
148
|
+
allSignatures = fetchSigsBackwards(opts, ctx) // generate backwards until depleting getSignaturesForAddress
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Process signatures
|
|
152
|
+
for await (const signatureInfo of allSignatures) {
|
|
153
|
+
yield await ctx.getTransaction(signatureInfo.signature)
|
|
154
|
+
}
|
|
155
|
+
}
|
package/src/solana/offchain.ts
CHANGED
|
@@ -2,13 +2,18 @@ import { type BN, BorshCoder } from '@coral-xyz/anchor'
|
|
|
2
2
|
import type { PublicKey } from '@solana/web3.js'
|
|
3
3
|
import { hexlify } from 'ethers'
|
|
4
4
|
|
|
5
|
+
import {
|
|
6
|
+
CCIPCctpDecodeError,
|
|
7
|
+
CCIPCctpMultipleEventsError,
|
|
8
|
+
CCIPDataFormatUnsupportedError,
|
|
9
|
+
} from '../errors/index.ts'
|
|
5
10
|
import { getUsdcAttestation } from '../offchain.ts'
|
|
6
11
|
import type { CCIPMessage, CCIPRequest, OffchainTokenData, WithLogger } from '../types.ts'
|
|
7
|
-
import { networkInfo, util } from '../utils.ts'
|
|
12
|
+
import { bytesToBuffer, networkInfo, util } from '../utils.ts'
|
|
8
13
|
import { IDL as BASE_TOKEN_POOL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
|
|
9
14
|
import { IDL as CCTP_TOKEN_POOL } from './idl/1.6.0/CCIP_CCTP_TOKEN_POOL.ts'
|
|
10
15
|
import type { SolanaLog, SolanaTransaction } from './index.ts'
|
|
11
|
-
import {
|
|
16
|
+
import { hexDiscriminator } from './utils.ts'
|
|
12
17
|
|
|
13
18
|
interface CcipCctpMessageSentEvent {
|
|
14
19
|
originalSender: PublicKey
|
|
@@ -53,7 +58,7 @@ export async function fetchSolanaOffchainTokenData(
|
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
if (request.message.tokenAmounts.length > 1) {
|
|
56
|
-
throw new
|
|
61
|
+
throw new CCIPDataFormatUnsupportedError(
|
|
57
62
|
`Expected at most 1 token transfer, found ${request.message.tokenAmounts?.length}`,
|
|
58
63
|
)
|
|
59
64
|
}
|
|
@@ -75,7 +80,7 @@ export async function fetchSolanaOffchainTokenData(
|
|
|
75
80
|
if (requestInvokeIdx >= l.index || l.index >= log.index) continue
|
|
76
81
|
if (l.topics[0] !== hexDiscriminator('CcipCctpMessageSentEvent')) continue
|
|
77
82
|
const decoded = cctpTokenPoolCoder.events.decode(l.data)
|
|
78
|
-
if (!decoded) throw new
|
|
83
|
+
if (!decoded) throw new CCIPCctpDecodeError(util.inspect(l))
|
|
79
84
|
cctpEvents.push(decoded.data as unknown as CcipCctpMessageSentEvent)
|
|
80
85
|
}
|
|
81
86
|
const offchainTokenData: OffchainTokenData[] = request.message.tokenAmounts.map(() => undefined)
|
|
@@ -88,9 +93,7 @@ export async function fetchSolanaOffchainTokenData(
|
|
|
88
93
|
|
|
89
94
|
// Currently, we only support ONE token per transfer
|
|
90
95
|
if (cctpEvents.length > 1) {
|
|
91
|
-
throw new
|
|
92
|
-
`Expected only 1 CcipCctpMessageSentEvent, found ${cctpEvents.length} in transaction ${txSignature}.`,
|
|
93
|
-
)
|
|
96
|
+
throw new CCIPCctpMultipleEventsError(cctpEvents.length, txSignature)
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
// NOTE: assuming USDC token is the first (and only) token in the CCIP message, we will process the CCTP event.
|
package/src/solana/patchBorsh.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { BorshInstructionCoder } from '@coral-xyz/anchor'
|
|
|
4
4
|
import { BorshTypesCoder } from '@coral-xyz/anchor/dist/cjs/coder/borsh/types.js'
|
|
5
5
|
import { sha256, toUtf8Bytes } from 'ethers'
|
|
6
6
|
|
|
7
|
+
import { CCIPBorshMethodUnknownError, CCIPBorshTypeUnknownError } from '../errors/index.ts'
|
|
7
8
|
import { snakeToCamel } from '../utils.ts'
|
|
8
9
|
import { camelToSnakeCase } from './utils.ts'
|
|
9
10
|
|
|
@@ -30,7 +31,7 @@ export function patchBorsh() {
|
|
|
30
31
|
name,
|
|
31
32
|
)
|
|
32
33
|
if (!layout) {
|
|
33
|
-
throw new
|
|
34
|
+
throw new CCIPBorshTypeUnknownError(name)
|
|
34
35
|
}
|
|
35
36
|
let buffer = Buffer.alloc(512)
|
|
36
37
|
let len
|
|
@@ -61,7 +62,7 @@ export function patchBorsh() {
|
|
|
61
62
|
methodName,
|
|
62
63
|
)
|
|
63
64
|
if (!layout) {
|
|
64
|
-
throw new
|
|
65
|
+
throw new CCIPBorshMethodUnknownError(methodName)
|
|
65
66
|
}
|
|
66
67
|
let buffer = Buffer.alloc(512)
|
|
67
68
|
let len
|
package/src/solana/send.ts
CHANGED
|
@@ -18,11 +18,18 @@ import BN from 'bn.js'
|
|
|
18
18
|
import { zeroPadValue } from 'ethers'
|
|
19
19
|
|
|
20
20
|
import { SolanaChain } from './index.ts'
|
|
21
|
+
import {
|
|
22
|
+
CCIPSolanaFeeResultInvalidError,
|
|
23
|
+
CCIPSolanaLookupTableNotFoundError,
|
|
24
|
+
CCIPSolanaRouterConfigNotFoundError,
|
|
25
|
+
CCIPTokenAmountInvalidError,
|
|
26
|
+
CCIPTokenMintNotFoundError,
|
|
27
|
+
} from '../errors/index.ts'
|
|
21
28
|
import { type AnyMessage, type WithLogger, ChainFamily } from '../types.ts'
|
|
22
|
-
import { toLeArray, util } from '../utils.ts'
|
|
29
|
+
import { bytesToBuffer, toLeArray, util } from '../utils.ts'
|
|
23
30
|
import { IDL as CCIP_ROUTER_IDL } from './idl/1.6.0/CCIP_ROUTER.ts'
|
|
24
31
|
import type { UnsignedSolanaTx } from './types.ts'
|
|
25
|
-
import {
|
|
32
|
+
import { simulationProvider } from './utils.ts'
|
|
26
33
|
|
|
27
34
|
function anyToSvmMessage(message: AnyMessage): IdlTypes<typeof CCIP_ROUTER_IDL>['SVM2AnyMessage'] {
|
|
28
35
|
const feeTokenPubkey = message.feeToken ? new PublicKey(message.feeToken) : PublicKey.default
|
|
@@ -32,7 +39,7 @@ function anyToSvmMessage(message: AnyMessage): IdlTypes<typeof CCIP_ROUTER_IDL>[
|
|
|
32
39
|
data: bytesToBuffer(message.data || '0x'),
|
|
33
40
|
tokenAmounts: (message.tokenAmounts || []).map((ta) => {
|
|
34
41
|
if (!ta.token || ta.amount < 0n) {
|
|
35
|
-
throw new
|
|
42
|
+
throw new CCIPTokenAmountInvalidError()
|
|
36
43
|
}
|
|
37
44
|
return {
|
|
38
45
|
token: new PublicKey(ta.token),
|
|
@@ -66,7 +73,7 @@ export async function getFee(
|
|
|
66
73
|
// Get router config to find feeQuoter
|
|
67
74
|
const [configPda] = PublicKey.findProgramAddressSync([Buffer.from('config')], program.programId)
|
|
68
75
|
const configAccount = await connection.getAccountInfo(configPda)
|
|
69
|
-
if (!configAccount) throw new
|
|
76
|
+
if (!configAccount) throw new CCIPSolanaRouterConfigNotFoundError(configPda.toBase58())
|
|
70
77
|
|
|
71
78
|
const { feeQuoter, linkTokenMint }: { feeQuoter: PublicKey; linkTokenMint: PublicKey } =
|
|
72
79
|
program.coder.accounts.decode('config', configAccount.data)
|
|
@@ -147,7 +154,7 @@ export async function getFee(
|
|
|
147
154
|
.view()) as IdlTypes<typeof CCIP_ROUTER_IDL>['GetFeeResult']
|
|
148
155
|
|
|
149
156
|
if (!result?.amount) {
|
|
150
|
-
throw new
|
|
157
|
+
throw new CCIPSolanaFeeResultInvalidError(util.inspect(result))
|
|
151
158
|
}
|
|
152
159
|
|
|
153
160
|
return BigInt(result.amount.toString())
|
|
@@ -224,7 +231,7 @@ async function deriveAccountsCcipSend({
|
|
|
224
231
|
const lookupTableAccountInfo = await connection.getAddressLookupTable(table)
|
|
225
232
|
|
|
226
233
|
if (!lookupTableAccountInfo.value) {
|
|
227
|
-
throw new
|
|
234
|
+
throw new CCIPSolanaLookupTableNotFoundError(table.toBase58())
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
return lookupTableAccountInfo.value
|
|
@@ -332,7 +339,7 @@ async function approveRouterSpender(
|
|
|
332
339
|
): Promise<TransactionInstruction | undefined> {
|
|
333
340
|
// Get the current account info to check existing delegation (or create if needed)
|
|
334
341
|
const mintInfo = await connection.getAccountInfo(token)
|
|
335
|
-
if (!mintInfo) throw new
|
|
342
|
+
if (!mintInfo) throw new CCIPTokenMintNotFoundError(token.toBase58())
|
|
336
343
|
const associatedTokenAccount = getAssociatedTokenAddressSync(
|
|
337
344
|
token,
|
|
338
345
|
owner,
|
package/src/solana/utils.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { eventDiscriminator } from '@coral-xyz/anchor'
|
|
1
|
+
import { type IdlTypes, eventDiscriminator } from '@coral-xyz/anchor'
|
|
4
2
|
import {
|
|
5
3
|
type AddressLookupTableAccount,
|
|
6
4
|
type Connection,
|
|
@@ -14,11 +12,17 @@ import {
|
|
|
14
12
|
TransactionMessage,
|
|
15
13
|
VersionedTransaction,
|
|
16
14
|
} from '@solana/web3.js'
|
|
17
|
-
import {
|
|
15
|
+
import { dataLength, dataSlice, hexlify } from 'ethers'
|
|
18
16
|
|
|
17
|
+
import {
|
|
18
|
+
CCIPSolanaComputeUnitsExceededError,
|
|
19
|
+
CCIPTransactionNotFinalizedError,
|
|
20
|
+
} from '../errors/index.ts'
|
|
19
21
|
import type { Log_, WithLogger } from '../types.ts'
|
|
20
22
|
import { getDataBytes, sleep } from '../utils.ts'
|
|
23
|
+
import type { IDL as BASE_TOKEN_POOL_IDL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
|
|
21
24
|
import type { UnsignedSolanaTx, Wallet } from './types.ts'
|
|
25
|
+
import type { RateLimiterState } from '../chain.ts'
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* Generates a hex-encoded discriminator for a Solana event.
|
|
@@ -29,15 +33,6 @@ export function hexDiscriminator(eventName: string): string {
|
|
|
29
33
|
return hexlify(eventDiscriminator(eventName))
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
/**
|
|
33
|
-
* Converts bytes to a Node.js Buffer.
|
|
34
|
-
* @param bytes - Bytes to convert.
|
|
35
|
-
* @returns Node.js Buffer.
|
|
36
|
-
*/
|
|
37
|
-
export function bytesToBuffer(bytes: BytesLike): Buffer {
|
|
38
|
-
return Buffer.from(getDataBytes(bytes).buffer)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
36
|
/**
|
|
42
37
|
* Waits for a Solana transaction to reach finalized status.
|
|
43
38
|
* @param connection - Solana connection instance.
|
|
@@ -61,7 +56,7 @@ export async function waitForFinalization(
|
|
|
61
56
|
await sleep(intervalMs)
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
throw new
|
|
59
|
+
throw new CCIPTransactionNotFinalizedError(signature)
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
/**
|
|
@@ -361,9 +356,7 @@ export async function simulateAndSendTxs(
|
|
|
361
356
|
} else if (!includesMain || computeUnits == null || simulated <= computeUnits) {
|
|
362
357
|
computeUnitLimit = Math.ceil(simulated * 1.1)
|
|
363
358
|
} else {
|
|
364
|
-
throw new
|
|
365
|
-
`Main simulation exceeds specified computeUnits limit. simulated=${simulated}, limit=${computeUnits}`,
|
|
366
|
-
)
|
|
359
|
+
throw new CCIPSolanaComputeUnitsExceededError(simulated, computeUnits)
|
|
367
360
|
}
|
|
368
361
|
break
|
|
369
362
|
} catch (err) {
|
|
@@ -394,3 +387,24 @@ export async function simulateAndSendTxs(
|
|
|
394
387
|
}
|
|
395
388
|
return mainHash!
|
|
396
389
|
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Convert TokenPool's rate limit to RateLimiterState object
|
|
393
|
+
*/
|
|
394
|
+
export function convertRateLimiter(
|
|
395
|
+
input: IdlTypes<typeof BASE_TOKEN_POOL_IDL>['BaseChain']['inboundRateLimit'],
|
|
396
|
+
): RateLimiterState {
|
|
397
|
+
if (!input.cfg.enabled) return null
|
|
398
|
+
const tokens = BigInt(input.tokens.toString())
|
|
399
|
+
const out: RateLimiterState = {
|
|
400
|
+
capacity: BigInt(input.cfg.capacity.toString()),
|
|
401
|
+
rate: BigInt(input.cfg.rate.toString()),
|
|
402
|
+
get tokens() {
|
|
403
|
+
const cur =
|
|
404
|
+
tokens + this.rate * BigInt(Math.floor(Date.now() / 1000) - input.lastUpdated.toNumber())
|
|
405
|
+
if (cur < this.capacity) return cur
|
|
406
|
+
else return this.capacity
|
|
407
|
+
},
|
|
408
|
+
}
|
|
409
|
+
return out
|
|
410
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { SuiClient } from '@mysten/sui/client'
|
|
2
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
3
|
+
import { normalizeSuiAddress } from '@mysten/sui/utils'
|
|
4
|
+
import { hexlify } from 'ethers'
|
|
5
|
+
|
|
6
|
+
import { CCIPError } from '../errors/CCIPError.ts'
|
|
7
|
+
import { CCIPErrorCode } from '../errors/codes.ts'
|
|
8
|
+
import { bytesToBuffer } from '../utils.ts'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Discovers the CCIP package ID associated with a given Sui onramp package.
|
|
12
|
+
*
|
|
13
|
+
* @param client - sui client
|
|
14
|
+
* @param onramp - sui onramp package id
|
|
15
|
+
* @returns ccip package id
|
|
16
|
+
*/
|
|
17
|
+
export const discoverCCIP = async (client: SuiClient, onramp: string): Promise<string> => {
|
|
18
|
+
const tx = new Transaction()
|
|
19
|
+
tx.moveCall({
|
|
20
|
+
target: `${onramp}::onramp::get_ccip_package_id`,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const inspectResult = await client.devInspectTransactionBlock({
|
|
24
|
+
sender: normalizeSuiAddress('0x0'),
|
|
25
|
+
transactionBlock: tx,
|
|
26
|
+
})
|
|
27
|
+
const returnValues = inspectResult.results?.[0]?.returnValues
|
|
28
|
+
if (!returnValues || returnValues.length === 0) {
|
|
29
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'No return values from dev inspect')
|
|
30
|
+
}
|
|
31
|
+
const [valueBytes] = returnValues[0]
|
|
32
|
+
if (!valueBytes) {
|
|
33
|
+
throw new CCIPError(
|
|
34
|
+
CCIPErrorCode.UNKNOWN,
|
|
35
|
+
'Unable to decode CCIP package id from return values',
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return normalizeSuiAddress(hexlify(bytesToBuffer(valueBytes)))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Gets the Sui offramp package ID associated with a given CCIP package ID.
|
|
44
|
+
*
|
|
45
|
+
* @param client - Sui client
|
|
46
|
+
* @param ccip - Sui CCIP Package Id
|
|
47
|
+
* @returns Sui offramp package id
|
|
48
|
+
*/
|
|
49
|
+
export const discoverOfframp = async (client: SuiClient, ccip: string) => {
|
|
50
|
+
// Get CCIP publish tx info
|
|
51
|
+
// Get the owner cap created in that tx.
|
|
52
|
+
// Get owner of the ownercap object.
|
|
53
|
+
// Get objects owned by that owner.
|
|
54
|
+
// Trough each of the objects owned by that owner, get the original transaction that created them.
|
|
55
|
+
// Take any of the objects created by that transaction, check its info to find the OffRamp package.
|
|
56
|
+
const ccipObject = await client.getObject({
|
|
57
|
+
id: ccip,
|
|
58
|
+
options: {
|
|
59
|
+
showPreviousTransaction: true,
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Get the tx that created the ownercap object.
|
|
64
|
+
const ccipCreationTxDigest = ccipObject.data?.previousTransaction
|
|
65
|
+
if (!ccipCreationTxDigest) {
|
|
66
|
+
throw new CCIPError(
|
|
67
|
+
CCIPErrorCode.UNKNOWN,
|
|
68
|
+
'Could not find previous transaction for CCIP object',
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ccipCreationTx = await client.getTransactionBlock({
|
|
73
|
+
digest: ccipCreationTxDigest,
|
|
74
|
+
options: {
|
|
75
|
+
showEffects: true,
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const ccipCreatedObjects = ccipCreationTx.effects?.created?.map((obj) => obj.reference.objectId)
|
|
80
|
+
if (!ccipCreatedObjects || ccipCreatedObjects.length === 0) {
|
|
81
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'No created objects found in creation transaction')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ccipObjectsData = await Promise.all(
|
|
85
|
+
ccipCreatedObjects.map((objId) =>
|
|
86
|
+
client.getObject({
|
|
87
|
+
id: objId,
|
|
88
|
+
options: {
|
|
89
|
+
showType: true,
|
|
90
|
+
showContent: true,
|
|
91
|
+
showOwner: true,
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const ownerCapObject = ccipObjectsData.find((objData) =>
|
|
98
|
+
objData.data?.type?.includes('::ownable::OwnerCap'),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if (!ownerCapObject) {
|
|
102
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'OwnerCap object not found among created objects')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ownerCapOwner = ownerCapObject.data?.owner
|
|
106
|
+
if (!ownerCapOwner) {
|
|
107
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'Could not find owner of the OwnerCap object')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (typeof ownerCapOwner === 'string' || !('AddressOwner' in ownerCapOwner)) {
|
|
111
|
+
throw new CCIPError(CCIPErrorCode.UNKNOWN, 'OwnerCap object does not have an AddressOwner')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const ownerCapOwnerObjects = await client.getOwnedObjects({
|
|
115
|
+
owner: ownerCapOwner['AddressOwner'],
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const fullObjectsInfo = await Promise.all(
|
|
119
|
+
ownerCapOwnerObjects.data.map((obj) =>
|
|
120
|
+
client.getObject({
|
|
121
|
+
id: obj.data?.objectId || '',
|
|
122
|
+
options: {
|
|
123
|
+
showType: true,
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const ownerCapPackageIds = fullObjectsInfo
|
|
130
|
+
.filter((objData) => objData.data?.type?.includes('::ownable::OwnerCap'))
|
|
131
|
+
.map((obj) => obj.data?.type?.split('::')[0])
|
|
132
|
+
|
|
133
|
+
const packagesInfo = await Promise.all(
|
|
134
|
+
ownerCapPackageIds.map((pkgId) =>
|
|
135
|
+
client.getNormalizedMoveModulesByPackage({
|
|
136
|
+
package: pkgId || '',
|
|
137
|
+
}),
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
const offrampPkgs = packagesInfo
|
|
142
|
+
.filter((pkg) => {
|
|
143
|
+
return Object.values(pkg).some((module) => module.name === 'offramp')
|
|
144
|
+
})
|
|
145
|
+
.flatMap((pkg) => Object.values(pkg))
|
|
146
|
+
.filter((module) => module.name === 'offramp')
|
|
147
|
+
|
|
148
|
+
if (offrampPkgs.length === 0) {
|
|
149
|
+
throw new CCIPError(
|
|
150
|
+
CCIPErrorCode.UNKNOWN,
|
|
151
|
+
'Could not find OffRamp package among OwnerCap packages',
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (offrampPkgs.length > 1) {
|
|
156
|
+
throw new CCIPError(
|
|
157
|
+
CCIPErrorCode.UNKNOWN,
|
|
158
|
+
'Multiple OffRamp packages found; unable to uniquely identify OffRamp package',
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return normalizeSuiAddress(offrampPkgs[0].address)
|
|
163
|
+
}
|