@chainlink/ccip-sdk 0.90.2 → 0.91.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 +35 -26
- package/dist/aptos/exec.d.ts +4 -5
- package/dist/aptos/exec.d.ts.map +1 -1
- package/dist/aptos/exec.js +5 -14
- package/dist/aptos/exec.js.map +1 -1
- package/dist/aptos/hasher.d.ts +18 -0
- package/dist/aptos/hasher.d.ts.map +1 -1
- package/dist/aptos/hasher.js +18 -0
- package/dist/aptos/hasher.js.map +1 -1
- package/dist/aptos/index.d.ts +127 -28
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +199 -70
- package/dist/aptos/index.js.map +1 -1
- package/dist/aptos/logs.d.ts +18 -0
- package/dist/aptos/logs.d.ts.map +1 -1
- package/dist/aptos/logs.js +21 -3
- package/dist/aptos/logs.js.map +1 -1
- package/dist/aptos/send.d.ts +22 -5
- package/dist/aptos/send.d.ts.map +1 -1
- package/dist/aptos/send.js +23 -15
- package/dist/aptos/send.js.map +1 -1
- package/dist/aptos/token.d.ts +6 -0
- package/dist/aptos/token.d.ts.map +1 -1
- package/dist/aptos/token.js +6 -0
- package/dist/aptos/token.js.map +1 -1
- package/dist/aptos/types.d.ts +16 -1
- package/dist/aptos/types.d.ts.map +1 -1
- package/dist/aptos/types.js +13 -0
- package/dist/aptos/types.js.map +1 -1
- package/dist/aptos/utils.d.ts +1 -1
- package/dist/aptos/utils.js +1 -1
- package/dist/chain.d.ts +185 -99
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +38 -15
- package/dist/chain.js.map +1 -1
- package/dist/commits.d.ts +4 -10
- package/dist/commits.d.ts.map +1 -1
- package/dist/commits.js +2 -1
- package/dist/commits.js.map +1 -1
- package/dist/evm/const.d.ts +5 -0
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +5 -0
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/errors.d.ts +5 -0
- package/dist/evm/errors.d.ts.map +1 -1
- package/dist/evm/errors.js +6 -1
- package/dist/evm/errors.js.map +1 -1
- package/dist/evm/hasher.d.ts +16 -2
- package/dist/evm/hasher.d.ts.map +1 -1
- package/dist/evm/hasher.js +17 -3
- package/dist/evm/hasher.js.map +1 -1
- package/dist/evm/index.d.ts +176 -31
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +312 -154
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/logs.d.ts +20 -0
- package/dist/evm/logs.d.ts.map +1 -0
- package/dist/evm/logs.js +194 -0
- package/dist/evm/logs.js.map +1 -0
- package/dist/evm/messages.d.ts +11 -2
- package/dist/evm/messages.d.ts.map +1 -1
- package/dist/evm/messages.js +4 -2
- package/dist/evm/messages.js.map +1 -1
- package/dist/evm/offchain.d.ts +7 -2
- package/dist/evm/offchain.d.ts.map +1 -1
- package/dist/evm/offchain.js +12 -7
- package/dist/evm/offchain.js.map +1 -1
- package/dist/execution.d.ts +19 -62
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +28 -31
- package/dist/execution.js.map +1 -1
- package/dist/extra-args.d.ts +35 -5
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +10 -5
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +6 -8
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +7 -9
- package/dist/gas.js.map +1 -1
- package/dist/hasher/common.d.ts +3 -2
- package/dist/hasher/common.d.ts.map +1 -1
- package/dist/hasher/common.js +2 -2
- package/dist/hasher/common.js.map +1 -1
- package/dist/hasher/hasher.d.ts +8 -2
- package/dist/hasher/hasher.d.ts.map +1 -1
- package/dist/hasher/hasher.js +8 -3
- package/dist/hasher/hasher.js.map +1 -1
- package/dist/hasher/merklemulti.d.ts +11 -9
- package/dist/hasher/merklemulti.d.ts.map +1 -1
- package/dist/hasher/merklemulti.js +17 -16
- package/dist/hasher/merklemulti.js.map +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +39 -25
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +42 -35
- package/dist/requests.js.map +1 -1
- package/dist/selectors.d.ts +1 -1
- package/dist/solana/cleanup.d.ts +14 -10
- package/dist/solana/cleanup.d.ts.map +1 -1
- package/dist/solana/cleanup.js +35 -33
- package/dist/solana/cleanup.js.map +1 -1
- package/dist/solana/exec.d.ts +19 -11
- package/dist/solana/exec.d.ts.map +1 -1
- package/dist/solana/exec.js +86 -163
- package/dist/solana/exec.js.map +1 -1
- package/dist/solana/hasher.d.ts +7 -2
- package/dist/solana/hasher.d.ts.map +1 -1
- package/dist/solana/hasher.js +7 -2
- package/dist/solana/hasher.js.map +1 -1
- package/dist/solana/index.d.ts +202 -84
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +367 -252
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/offchain.d.ts +8 -18
- package/dist/solana/offchain.d.ts.map +1 -1
- package/dist/solana/offchain.js +29 -83
- package/dist/solana/offchain.js.map +1 -1
- package/dist/solana/patchBorsh.d.ts +5 -1
- package/dist/solana/patchBorsh.d.ts.map +1 -1
- package/dist/solana/patchBorsh.js +57 -46
- package/dist/solana/patchBorsh.js.map +1 -1
- package/dist/solana/send.d.ts +28 -10
- package/dist/solana/send.d.ts.map +1 -1
- package/dist/solana/send.js +44 -77
- package/dist/solana/send.js.map +1 -1
- package/dist/solana/types.d.ts +22 -1
- package/dist/solana/types.d.ts.map +1 -1
- package/dist/solana/types.js +12 -1
- package/dist/solana/types.js.map +1 -1
- package/dist/solana/utils.d.ts +58 -4
- package/dist/solana/utils.d.ts.map +1 -1
- package/dist/solana/utils.js +110 -7
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/hasher.d.ts +18 -0
- package/dist/sui/hasher.d.ts.map +1 -1
- package/dist/sui/hasher.js +18 -0
- package/dist/sui/hasher.js.map +1 -1
- package/dist/sui/index.d.ts +99 -12
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +108 -19
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/types.d.ts +6 -0
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js +5 -0
- package/dist/sui/types.js.map +1 -1
- package/dist/supported-chains.d.ts +2 -1
- package/dist/supported-chains.d.ts.map +1 -1
- package/dist/supported-chains.js.map +1 -1
- package/dist/types.d.ts +127 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +67 -46
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +143 -21
- package/dist/utils.js.map +1 -1
- package/package.json +13 -9
- package/src/aptos/exec.ts +7 -18
- package/src/aptos/hasher.ts +18 -0
- package/src/aptos/index.ts +288 -110
- package/src/aptos/logs.ts +21 -3
- package/src/aptos/send.ts +25 -22
- package/src/aptos/token.ts +6 -0
- package/src/aptos/types.ts +26 -2
- package/src/aptos/utils.ts +1 -1
- package/src/chain.ts +243 -108
- package/src/commits.ts +6 -7
- package/src/evm/const.ts +5 -0
- package/src/evm/errors.ts +6 -1
- package/src/evm/hasher.ts +20 -4
- package/src/evm/index.ts +416 -214
- package/src/evm/logs.ts +255 -0
- package/src/evm/messages.ts +11 -5
- package/src/evm/offchain.ts +13 -4
- package/src/execution.ts +40 -32
- package/src/extra-args.ts +38 -6
- package/src/gas.ts +7 -9
- package/src/hasher/common.ts +3 -2
- package/src/hasher/hasher.ts +12 -4
- package/src/hasher/merklemulti.ts +17 -16
- package/src/index.ts +29 -23
- package/src/requests.ts +64 -46
- package/src/selectors.ts +1 -1
- package/src/solana/cleanup.ts +49 -34
- package/src/solana/exec.ts +128 -272
- package/src/solana/hasher.ts +13 -4
- package/src/solana/index.ts +483 -356
- package/src/solana/offchain.ts +32 -102
- package/src/solana/patchBorsh.ts +65 -50
- package/src/solana/send.ts +52 -111
- package/src/solana/types.ts +44 -3
- package/src/solana/utils.ts +143 -19
- package/src/sui/hasher.ts +18 -0
- package/src/sui/index.ts +143 -31
- package/src/sui/types.ts +6 -0
- package/src/supported-chains.ts +2 -1
- package/src/types.ts +130 -18
- package/src/utils.ts +168 -26
- package/tsconfig.json +2 -1
package/src/solana/exec.ts
CHANGED
|
@@ -1,119 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Buffer } from 'buffer'
|
|
2
|
+
|
|
3
|
+
import { type IdlTypes, Program } from '@coral-xyz/anchor'
|
|
2
4
|
import {
|
|
3
5
|
type AccountMeta,
|
|
4
|
-
type
|
|
6
|
+
type Connection,
|
|
5
7
|
type TransactionInstruction,
|
|
6
8
|
AddressLookupTableAccount,
|
|
7
9
|
AddressLookupTableProgram,
|
|
8
|
-
ComputeBudgetProgram,
|
|
9
10
|
PublicKey,
|
|
10
|
-
SendTransactionError,
|
|
11
11
|
SystemProgram,
|
|
12
|
-
TransactionExpiredBlockheightExceededError,
|
|
13
|
-
TransactionMessage,
|
|
14
|
-
VersionedTransaction,
|
|
15
12
|
} from '@solana/web3.js'
|
|
16
13
|
import BN from 'bn.js'
|
|
17
14
|
import { hexlify } from 'ethers'
|
|
18
15
|
|
|
19
|
-
import type
|
|
16
|
+
import { type ExecutionReport, type WithLogger, ChainFamily } from '../types.ts'
|
|
20
17
|
import { IDL as CCIP_OFFRAMP_IDL } from './idl/1.6.0/CCIP_OFFRAMP.ts'
|
|
21
18
|
import { encodeSolanaOffchainTokenData } from './offchain.ts'
|
|
22
|
-
import type { CCIPMessage_V1_6_Solana } from './types.ts'
|
|
23
|
-
import
|
|
24
|
-
import {
|
|
25
|
-
import { bytesToBuffer, simulateTransaction, simulationProvider } from './utils.ts'
|
|
26
|
-
import './patchBorsh.ts'
|
|
27
|
-
|
|
28
|
-
type ExecStepTx = readonly [reason: string, transactions: VersionedTransaction]
|
|
19
|
+
import type { CCIPMessage_V1_6_Solana, UnsignedSolanaTx } from './types.ts'
|
|
20
|
+
import { getDataBytes, toLeArray } from '../utils.ts'
|
|
21
|
+
import { bytesToBuffer } from './utils.ts'
|
|
29
22
|
|
|
30
23
|
type ExecAlt = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function executeReport({
|
|
37
|
-
offrampProgram,
|
|
38
|
-
execReport,
|
|
39
|
-
...opts
|
|
40
|
-
}: {
|
|
41
|
-
offrampProgram: Program<typeof CCIP_OFFRAMP_IDL>
|
|
42
|
-
execReport: ExecutionReport<CCIPMessage_V1_6_Solana>
|
|
43
|
-
gasLimit?: number
|
|
44
|
-
forceLookupTable?: boolean
|
|
45
|
-
forceBuffer?: boolean
|
|
46
|
-
clearLeftoverAccounts?: boolean
|
|
47
|
-
}): Promise<Pick<ChainTransaction, 'hash'>> {
|
|
48
|
-
const provider = offrampProgram.provider as AnchorProvider
|
|
49
|
-
const wallet = provider.wallet
|
|
50
|
-
const connection = provider.connection
|
|
51
|
-
|
|
52
|
-
const execTxs = await buildExecTxToSolana(offrampProgram, execReport, opts?.gasLimit, opts)
|
|
53
|
-
|
|
54
|
-
let execTxSignature: string, signature: string
|
|
55
|
-
for (const [i, [reason, transaction]] of execTxs.entries()) {
|
|
56
|
-
// Refresh the blockhash for each transaction, as the blockhash is only valid for a short time
|
|
57
|
-
// and we spend a lot of time waiting for finalization of the previous transactions.
|
|
58
|
-
transaction.message.recentBlockhash = (await connection.getLatestBlockhash()).blockhash
|
|
59
|
-
|
|
60
|
-
const signed = await wallet.signTransaction(transaction)
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
signature = await connection.sendTransaction(signed)
|
|
64
|
-
|
|
65
|
-
if (reason === 'exec') execTxSignature = signature
|
|
66
|
-
} catch (e) {
|
|
67
|
-
if (
|
|
68
|
-
e instanceof SendTransactionError &&
|
|
69
|
-
e.logs?.some((log) =>
|
|
70
|
-
log.includes('Error Code: ExecutionReportBufferAlreadyContainsChunk.'),
|
|
71
|
-
)
|
|
72
|
-
) {
|
|
73
|
-
console.warn(
|
|
74
|
-
`Skipping tx ${i + 1} of ${execTxs.length} because a chunk is already in the buffer.`,
|
|
75
|
-
)
|
|
76
|
-
continue
|
|
77
|
-
} else {
|
|
78
|
-
throw e
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
console.debug(`Confirming tx #${i + 1} of ${execTxs.length}: ${signature} (${reason})...`)
|
|
83
|
-
for (let currentAttempt = 0; ; currentAttempt++) {
|
|
84
|
-
try {
|
|
85
|
-
const latestBlockhash = await connection.getLatestBlockhash()
|
|
86
|
-
await connection.confirmTransaction(
|
|
87
|
-
{
|
|
88
|
-
signature,
|
|
89
|
-
blockhash: latestBlockhash.blockhash,
|
|
90
|
-
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
|
91
|
-
},
|
|
92
|
-
'confirmed',
|
|
93
|
-
)
|
|
94
|
-
break
|
|
95
|
-
} catch (e) {
|
|
96
|
-
if (currentAttempt < 5 && e instanceof TransactionExpiredBlockheightExceededError) {
|
|
97
|
-
await sleep(1000)
|
|
98
|
-
} else {
|
|
99
|
-
throw e
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return { hash: execTxSignature! }
|
|
24
|
+
initialIxs: TransactionInstruction[]
|
|
25
|
+
lookupTable: AddressLookupTableAccount
|
|
26
|
+
finalIxs: TransactionInstruction[]
|
|
106
27
|
}
|
|
107
28
|
|
|
108
|
-
|
|
109
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Generate unsigned tx to execute a CCIP report on Solana.
|
|
31
|
+
* @param ctx - Context containing connection and logger
|
|
32
|
+
* @param payer - Payer of the transaction.
|
|
33
|
+
* @param offramp - Address of the OffRamp contract.
|
|
34
|
+
* @param execReport - Execution report.
|
|
35
|
+
* @param opts - Options for txs to be generated
|
|
36
|
+
* - forceBuffer - Sends report in chunks for buffering in offRamp before execution
|
|
37
|
+
* - forceLookupTable - Creates lookup table for execution transaction, and deactivates in the end
|
|
38
|
+
* - clearLeftoverAccounts - Resets buffer before filling it in
|
|
39
|
+
* @returns Transaction hash of the execution.
|
|
40
|
+
*/
|
|
41
|
+
export async function generateUnsignedExecuteReport(
|
|
42
|
+
ctx: { connection: Connection } & WithLogger,
|
|
43
|
+
payer: PublicKey,
|
|
44
|
+
offramp: PublicKey,
|
|
110
45
|
execReport: ExecutionReport<CCIPMessage_V1_6_Solana>,
|
|
111
|
-
computeUnitsOverride: number | undefined,
|
|
112
46
|
opts?: { forceLookupTable?: boolean; forceBuffer?: boolean; clearLeftoverAccounts?: boolean },
|
|
113
|
-
): Promise<
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const payerAddress = provider.wallet.publicKey
|
|
47
|
+
): Promise<UnsignedSolanaTx> {
|
|
48
|
+
const { connection, logger = console } = ctx
|
|
49
|
+
const program = new Program(CCIP_OFFRAMP_IDL, offramp, ctx)
|
|
117
50
|
|
|
118
51
|
let bufferId
|
|
119
52
|
if (opts?.forceBuffer) {
|
|
@@ -127,15 +60,16 @@ async function buildExecTxToSolana(
|
|
|
127
60
|
accounts,
|
|
128
61
|
addressLookupTables,
|
|
129
62
|
} = await getManuallyExecuteInputs({
|
|
63
|
+
payer,
|
|
64
|
+
offramp: program,
|
|
130
65
|
execReport,
|
|
131
|
-
offrampProgram,
|
|
132
|
-
transmitter: payerAddress.toBase58(),
|
|
133
66
|
bufferId,
|
|
67
|
+
logger,
|
|
134
68
|
})
|
|
135
69
|
|
|
136
70
|
const addressLookupTableAccounts = await Promise.all(
|
|
137
71
|
addressLookupTables.map(async (acc) => {
|
|
138
|
-
const lookupTableAccountInfo = await
|
|
72
|
+
const lookupTableAccountInfo = await connection.getAddressLookupTable(acc)
|
|
139
73
|
|
|
140
74
|
if (!lookupTableAccountInfo.value) {
|
|
141
75
|
throw new Error(`Lookup table account not found: ${acc.toBase58()}`)
|
|
@@ -145,35 +79,30 @@ async function buildExecTxToSolana(
|
|
|
145
79
|
}),
|
|
146
80
|
)
|
|
147
81
|
|
|
148
|
-
let serializedReport =
|
|
149
|
-
'ExecutionReportSingleChain',
|
|
150
|
-
preparedReport,
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
const { blockhash: recentBlockhash } = await provider.connection.getLatestBlockhash()
|
|
82
|
+
let serializedReport = program.coder.types.encode('ExecutionReportSingleChain', preparedReport)
|
|
154
83
|
|
|
155
84
|
let alt
|
|
156
85
|
if (opts?.forceLookupTable) {
|
|
157
|
-
alt = await
|
|
158
|
-
|
|
86
|
+
alt = await buildLookupTableIxs(
|
|
87
|
+
ctx,
|
|
88
|
+
payer,
|
|
89
|
+
accounts.map((acc) => acc.pubkey),
|
|
90
|
+
)
|
|
91
|
+
addressLookupTableAccounts.push(alt.lookupTable)
|
|
159
92
|
}
|
|
160
93
|
|
|
161
|
-
const
|
|
94
|
+
const instructions: TransactionInstruction[] = []
|
|
162
95
|
if (bufferId) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
bufferId,
|
|
170
|
-
opts,
|
|
171
|
-
)),
|
|
172
|
-
)
|
|
96
|
+
logger.info(`Execute report will be pre-buffered through the offramp. This may take some time.`)
|
|
97
|
+
const bufferingIxs = await bufferedTransactionData(payer, program, serializedReport, bufferId, {
|
|
98
|
+
logger,
|
|
99
|
+
...opts,
|
|
100
|
+
})
|
|
101
|
+
instructions.push(...bufferingIxs)
|
|
173
102
|
serializedReport = Buffer.from([]) // clear 1st param to manuallyExecute method if buffered
|
|
174
103
|
}
|
|
175
104
|
|
|
176
|
-
const
|
|
105
|
+
const execIx = await program.methods
|
|
177
106
|
.manuallyExecute(serializedReport, tokenIndexes)
|
|
178
107
|
.accounts({
|
|
179
108
|
config: accounts[0].pubkey,
|
|
@@ -190,61 +119,40 @@ async function buildExecTxToSolana(
|
|
|
190
119
|
rmnRemoteConfig: accounts[11].pubkey,
|
|
191
120
|
})
|
|
192
121
|
.remainingAccounts(accounts.slice(12))
|
|
193
|
-
.
|
|
194
|
-
|
|
195
|
-
computeUnitsOverride ||= Math.ceil(
|
|
196
|
-
1.1 *
|
|
197
|
-
((
|
|
198
|
-
await simulateTransaction({
|
|
199
|
-
instructions: execTx.instructions,
|
|
200
|
-
connection: provider.connection,
|
|
201
|
-
payerKey: provider.wallet.publicKey,
|
|
202
|
-
addressLookupTableAccounts,
|
|
203
|
-
computeUnitsOverride,
|
|
204
|
-
})
|
|
205
|
-
).unitsConsumed || 0),
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
// Add compute budget instruction at the beginning of instructions
|
|
209
|
-
execTx.instructions.unshift(
|
|
210
|
-
ComputeBudgetProgram.setComputeUnitLimit({
|
|
211
|
-
units: computeUnitsOverride,
|
|
212
|
-
}),
|
|
213
|
-
)
|
|
122
|
+
.instruction()
|
|
214
123
|
|
|
215
124
|
// actual exec tx
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
toVersionedTransaction(
|
|
219
|
-
execTx.instructions,
|
|
220
|
-
provider.wallet.publicKey,
|
|
221
|
-
recentBlockhash,
|
|
222
|
-
addressLookupTableAccounts,
|
|
223
|
-
),
|
|
224
|
-
])
|
|
125
|
+
let execIndex = instructions.length
|
|
126
|
+
instructions.push(execIx)
|
|
225
127
|
|
|
128
|
+
// "sandwich" instructions with ALT create+extend, then deactivate
|
|
226
129
|
if (alt) {
|
|
227
|
-
|
|
228
|
-
|
|
130
|
+
instructions.unshift(...alt.initialIxs)
|
|
131
|
+
execIndex += alt.initialIxs.length
|
|
132
|
+
instructions.push(...alt.finalIxs)
|
|
229
133
|
}
|
|
230
134
|
|
|
231
|
-
return
|
|
135
|
+
return {
|
|
136
|
+
family: ChainFamily.Solana,
|
|
137
|
+
instructions,
|
|
138
|
+
lookupTables: addressLookupTableAccounts,
|
|
139
|
+
mainIndex: execIndex,
|
|
140
|
+
}
|
|
232
141
|
}
|
|
233
142
|
|
|
234
|
-
async function
|
|
235
|
-
|
|
236
|
-
|
|
143
|
+
async function buildLookupTableIxs(
|
|
144
|
+
{ connection, logger = console }: { connection: Connection } & WithLogger,
|
|
145
|
+
authority: PublicKey,
|
|
146
|
+
addresses: PublicKey[],
|
|
237
147
|
): Promise<ExecAlt> {
|
|
238
|
-
const recentSlot = await
|
|
148
|
+
const recentSlot = await connection.getSlot('confirmed')
|
|
239
149
|
|
|
240
150
|
const [createIx, altAddr] = AddressLookupTableProgram.createLookupTable({
|
|
241
|
-
authority
|
|
242
|
-
payer:
|
|
151
|
+
authority,
|
|
152
|
+
payer: authority,
|
|
243
153
|
recentSlot,
|
|
244
154
|
})
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const addresses = accounts.map((a) => a.pubkey)
|
|
155
|
+
logger.info('Using Address Lookup Table', altAddr.toBase58())
|
|
248
156
|
|
|
249
157
|
if (addresses.length > 256) {
|
|
250
158
|
throw new Error(
|
|
@@ -257,7 +165,6 @@ async function buildLookupTableTxs(
|
|
|
257
165
|
const firstChunkLength = 28
|
|
258
166
|
const maxAddressesPerTx = 35
|
|
259
167
|
const extendIxs: TransactionInstruction[] = []
|
|
260
|
-
const ranges: [number, number][] = []
|
|
261
168
|
for (
|
|
262
169
|
let [start, end] = [0, firstChunkLength];
|
|
263
170
|
start < addresses.length;
|
|
@@ -265,18 +172,17 @@ async function buildLookupTableTxs(
|
|
|
265
172
|
) {
|
|
266
173
|
const addressesChunk = addresses.slice(start, end)
|
|
267
174
|
const extendIx = AddressLookupTableProgram.extendLookupTable({
|
|
268
|
-
|
|
269
|
-
|
|
175
|
+
authority,
|
|
176
|
+
payer: authority,
|
|
270
177
|
lookupTable: altAddr,
|
|
271
178
|
addresses: addressesChunk,
|
|
272
179
|
})
|
|
273
180
|
extendIxs.push(extendIx)
|
|
274
|
-
ranges.push([start, start + addressesChunk.length - 1])
|
|
275
181
|
}
|
|
276
182
|
|
|
277
183
|
const deactivateIx = AddressLookupTableProgram.deactivateLookupTable({
|
|
278
184
|
lookupTable: altAddr,
|
|
279
|
-
authority
|
|
185
|
+
authority,
|
|
280
186
|
})
|
|
281
187
|
|
|
282
188
|
// disable closeTx, to be cleaned in SolanaChain.cleanUpBuffers
|
|
@@ -286,87 +192,59 @@ async function buildLookupTableTxs(
|
|
|
286
192
|
// lookupTable: altAddr,
|
|
287
193
|
// })
|
|
288
194
|
|
|
289
|
-
const { blockhash: recentBlockhash } = await provider.connection.getLatestBlockhash()
|
|
290
|
-
|
|
291
195
|
return {
|
|
292
|
-
|
|
196
|
+
lookupTable: new AddressLookupTableAccount({
|
|
293
197
|
key: altAddr,
|
|
294
198
|
state: {
|
|
295
|
-
deactivationSlot:
|
|
199
|
+
deactivationSlot: 2n ** 64n - 1n,
|
|
296
200
|
lastExtendedSlot: recentSlot,
|
|
297
201
|
lastExtendedSlotStartIndex: 0,
|
|
298
202
|
addresses,
|
|
299
203
|
},
|
|
300
204
|
}),
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
[
|
|
304
|
-
`lookup[create + 0..${ranges[0][1]}]`,
|
|
305
|
-
toVersionedTransaction(
|
|
306
|
-
[createIx, extendIxs[0]],
|
|
307
|
-
provider.wallet.publicKey,
|
|
308
|
-
recentBlockhash,
|
|
309
|
-
),
|
|
310
|
-
],
|
|
311
|
-
...extendIxs
|
|
312
|
-
.slice(1)
|
|
313
|
-
.map<ExecStepTx>((ix, i) => [
|
|
314
|
-
`lookup[${ranges[i + 1][0]}..${ranges[i + 1][1]}]`,
|
|
315
|
-
toVersionedTransaction([ix], provider.wallet.publicKey, recentBlockhash),
|
|
316
|
-
]),
|
|
317
|
-
],
|
|
318
|
-
finalTxs: [
|
|
319
|
-
[
|
|
320
|
-
`lookup[deactivate]`,
|
|
321
|
-
toVersionedTransaction([deactivateIx], provider.wallet.publicKey, recentBlockhash),
|
|
322
|
-
],
|
|
323
|
-
],
|
|
205
|
+
initialIxs: [createIx, ...extendIxs],
|
|
206
|
+
finalIxs: [deactivateIx],
|
|
324
207
|
}
|
|
325
208
|
}
|
|
326
209
|
|
|
327
210
|
async function bufferedTransactionData(
|
|
328
|
-
|
|
211
|
+
payer: PublicKey,
|
|
212
|
+
offramp: Program<typeof CCIP_OFFRAMP_IDL>,
|
|
329
213
|
serializedReport: Buffer,
|
|
330
|
-
recentBlockhash: string,
|
|
331
214
|
bufferId: Buffer,
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
215
|
+
{
|
|
216
|
+
logger = console,
|
|
217
|
+
clearLeftoverAccounts,
|
|
218
|
+
}: { clearLeftoverAccounts?: boolean } & WithLogger = {},
|
|
219
|
+
): Promise<TransactionInstruction[]> {
|
|
336
220
|
const [bufferAddress] = PublicKey.findProgramAddressSync(
|
|
337
|
-
[Buffer.from('execution_report_buffer'), bufferId,
|
|
338
|
-
|
|
221
|
+
[Buffer.from('execution_report_buffer'), bufferId, payer.toBuffer()],
|
|
222
|
+
offramp.programId,
|
|
339
223
|
)
|
|
340
224
|
|
|
341
|
-
const [configPDA] = PublicKey.findProgramAddressSync(
|
|
342
|
-
[Buffer.from('config')],
|
|
343
|
-
offrampProgram.programId,
|
|
344
|
-
)
|
|
225
|
+
const [configPDA] = PublicKey.findProgramAddressSync([Buffer.from('config')], offramp.programId)
|
|
345
226
|
|
|
346
|
-
|
|
227
|
+
logger.info(
|
|
347
228
|
`The bufferID is ${hexlify(bufferId)}, and the PDA address for the buffer is ${bufferAddress.toString()}\nIf this buffering process is aborted, remember to cleanUp the account to recover locked rent.`,
|
|
348
229
|
)
|
|
349
230
|
|
|
350
231
|
const chunkSize = 800
|
|
351
|
-
const
|
|
232
|
+
const bufferedExecIxs: TransactionInstruction[] = []
|
|
352
233
|
|
|
353
234
|
const bufferingAccounts = {
|
|
354
235
|
executionReportBuffer: bufferAddress,
|
|
355
236
|
config: configPDA,
|
|
356
|
-
authority:
|
|
237
|
+
authority: payer,
|
|
357
238
|
systemProgram: SystemProgram.programId,
|
|
358
239
|
}
|
|
359
240
|
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
'buffering[clear]',
|
|
368
|
-
toVersionedTransaction(clearTx, provider.wallet.publicKey, recentBlockhash),
|
|
369
|
-
])
|
|
241
|
+
if (clearLeftoverAccounts) {
|
|
242
|
+
bufferedExecIxs.push(
|
|
243
|
+
await offramp.methods
|
|
244
|
+
.closeExecutionReportBuffer(bufferId)
|
|
245
|
+
.accounts(bufferingAccounts)
|
|
246
|
+
.instruction(),
|
|
247
|
+
)
|
|
370
248
|
}
|
|
371
249
|
|
|
372
250
|
const numChunks = Math.ceil(serializedReport.length / chunkSize)
|
|
@@ -374,42 +252,29 @@ async function bufferedTransactionData(
|
|
|
374
252
|
const end = Math.min(i + chunkSize, serializedReport.length)
|
|
375
253
|
const chunk: Buffer = serializedReport.subarray(i, end)
|
|
376
254
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
toVersionedTransaction(appendTx, provider.wallet.publicKey, recentBlockhash),
|
|
384
|
-
])
|
|
255
|
+
bufferedExecIxs.push(
|
|
256
|
+
await offramp.methods
|
|
257
|
+
.bufferExecutionReport(bufferId, serializedReport.length, chunk, i / chunkSize, numChunks)
|
|
258
|
+
.accounts(bufferingAccounts)
|
|
259
|
+
.instruction(),
|
|
260
|
+
)
|
|
385
261
|
}
|
|
386
262
|
|
|
387
|
-
return
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function toVersionedTransaction(
|
|
391
|
-
input: Transaction | TransactionInstruction[],
|
|
392
|
-
payerKey: PublicKey,
|
|
393
|
-
recentBlockhash: string,
|
|
394
|
-
addressLookupTableAccounts?: AddressLookupTableAccount[],
|
|
395
|
-
): VersionedTransaction {
|
|
396
|
-
const instructions: TransactionInstruction[] = Array.isArray(input) ? input : input.instructions
|
|
397
|
-
|
|
398
|
-
const message = new TransactionMessage({ payerKey, recentBlockhash, instructions })
|
|
399
|
-
return new VersionedTransaction(message.compileToV0Message(addressLookupTableAccounts))
|
|
263
|
+
return bufferedExecIxs
|
|
400
264
|
}
|
|
401
265
|
|
|
402
266
|
async function getManuallyExecuteInputs({
|
|
267
|
+
payer,
|
|
268
|
+
offramp,
|
|
403
269
|
execReport,
|
|
404
|
-
offrampProgram,
|
|
405
|
-
transmitter,
|
|
406
270
|
bufferId,
|
|
271
|
+
...ctx
|
|
407
272
|
}: {
|
|
273
|
+
payer: PublicKey
|
|
274
|
+
offramp: Program<typeof CCIP_OFFRAMP_IDL>
|
|
408
275
|
execReport: ExecutionReport<CCIPMessage_V1_6_Solana>
|
|
409
|
-
offrampProgram: Program<typeof CCIP_OFFRAMP_IDL>
|
|
410
|
-
transmitter: string
|
|
411
276
|
bufferId?: Buffer
|
|
412
|
-
}) {
|
|
277
|
+
} & WithLogger) {
|
|
413
278
|
const executionReport = prepareExecutionReport(execReport)
|
|
414
279
|
|
|
415
280
|
const messageAccountMetas = execReport.message.accounts.map((acc, index) => {
|
|
@@ -430,8 +295,6 @@ async function getManuallyExecuteInputs({
|
|
|
430
295
|
isWritable: false,
|
|
431
296
|
}
|
|
432
297
|
|
|
433
|
-
console.debug('Message receiver:', execReport.message.receiver)
|
|
434
|
-
|
|
435
298
|
// Prepend receiver to messaging accounts
|
|
436
299
|
const messagingAccounts: AccountMeta[] =
|
|
437
300
|
execReport.message.receiver !== PublicKey.default.toBase58()
|
|
@@ -457,15 +320,16 @@ async function getManuallyExecuteInputs({
|
|
|
457
320
|
addressLookupTableAccounts: addressLookupTables,
|
|
458
321
|
tokenIndexes,
|
|
459
322
|
} = await autoDeriveExecutionAccounts({
|
|
460
|
-
|
|
323
|
+
offramp,
|
|
461
324
|
originalSender: bytesToBuffer(execReport.message.sender),
|
|
462
|
-
|
|
325
|
+
payer,
|
|
463
326
|
messagingAccounts,
|
|
464
327
|
sourceChainSelector: execReport.message.header.sourceChainSelector,
|
|
465
328
|
tokenTransferAndOffchainData,
|
|
466
329
|
merkleRoot: bytesToBuffer(execReport.merkleRoot),
|
|
467
330
|
bufferId,
|
|
468
331
|
tokenReceiver: new PublicKey(execReport.message.tokenReceiver),
|
|
332
|
+
...ctx,
|
|
469
333
|
})
|
|
470
334
|
|
|
471
335
|
return {
|
|
@@ -518,19 +382,20 @@ function prepareExecutionReport({
|
|
|
518
382
|
}
|
|
519
383
|
|
|
520
384
|
async function autoDeriveExecutionAccounts({
|
|
521
|
-
|
|
385
|
+
offramp,
|
|
522
386
|
originalSender,
|
|
523
|
-
|
|
387
|
+
payer,
|
|
524
388
|
messagingAccounts,
|
|
525
389
|
sourceChainSelector,
|
|
526
390
|
tokenTransferAndOffchainData,
|
|
527
391
|
merkleRoot,
|
|
528
392
|
tokenReceiver,
|
|
529
393
|
bufferId,
|
|
394
|
+
logger = console,
|
|
530
395
|
}: {
|
|
531
|
-
|
|
396
|
+
offramp: Program<typeof CCIP_OFFRAMP_IDL>
|
|
532
397
|
originalSender: Buffer
|
|
533
|
-
|
|
398
|
+
payer: PublicKey
|
|
534
399
|
messagingAccounts: IdlTypes<typeof CCIP_OFFRAMP_IDL>['CcipAccountMeta'][]
|
|
535
400
|
sourceChainSelector: bigint
|
|
536
401
|
tokenTransferAndOffchainData: Array<
|
|
@@ -539,7 +404,7 @@ async function autoDeriveExecutionAccounts({
|
|
|
539
404
|
merkleRoot: Buffer
|
|
540
405
|
tokenReceiver: PublicKey
|
|
541
406
|
bufferId?: Buffer
|
|
542
|
-
}) {
|
|
407
|
+
} & WithLogger) {
|
|
543
408
|
const derivedAccounts: AccountMeta[] = []
|
|
544
409
|
const lookupTables: PublicKey[] = []
|
|
545
410
|
const tokenIndices: number[] = []
|
|
@@ -547,14 +412,11 @@ async function autoDeriveExecutionAccounts({
|
|
|
547
412
|
let stage = 'Start'
|
|
548
413
|
let tokenIndex = 0
|
|
549
414
|
|
|
550
|
-
const [configPDA] = PublicKey.findProgramAddressSync(
|
|
551
|
-
[Buffer.from('config')],
|
|
552
|
-
offrampProgram.programId,
|
|
553
|
-
)
|
|
415
|
+
const [configPDA] = PublicKey.findProgramAddressSync([Buffer.from('config')], offramp.programId)
|
|
554
416
|
|
|
555
417
|
while (true) {
|
|
556
418
|
const params: IdlTypes<typeof CCIP_OFFRAMP_IDL>['DeriveAccountsExecuteParams'] = {
|
|
557
|
-
executeCaller:
|
|
419
|
+
executeCaller: payer,
|
|
558
420
|
messageAccounts: messagingAccounts,
|
|
559
421
|
sourceChainSelector: new BN(sourceChainSelector.toString()),
|
|
560
422
|
originalSender: originalSender,
|
|
@@ -574,14 +436,8 @@ async function autoDeriveExecutionAccounts({
|
|
|
574
436
|
}))
|
|
575
437
|
}
|
|
576
438
|
|
|
577
|
-
// copy of Program which avoids signing every simulation
|
|
578
|
-
const readOnlyProgram = new Program(
|
|
579
|
-
offrampProgram.idl,
|
|
580
|
-
offrampProgram.programId,
|
|
581
|
-
simulationProvider(offrampProgram.provider.connection, transmitter),
|
|
582
|
-
)
|
|
583
439
|
// Execute as a view call to get the response
|
|
584
|
-
const response = (await
|
|
440
|
+
const response = (await offramp.methods
|
|
585
441
|
.deriveAccountsExecute(params, stage)
|
|
586
442
|
.accounts({
|
|
587
443
|
config: configPDA,
|
|
@@ -589,8 +445,8 @@ async function autoDeriveExecutionAccounts({
|
|
|
589
445
|
.remainingAccounts(askWith)
|
|
590
446
|
.view()
|
|
591
447
|
.catch((error: unknown) => {
|
|
592
|
-
|
|
593
|
-
|
|
448
|
+
logger.error('Error deriving accounts:', error)
|
|
449
|
+
logger.error('Params:', params)
|
|
594
450
|
throw error as Error
|
|
595
451
|
})) as IdlTypes<typeof CCIP_OFFRAMP_IDL>['DeriveAccountsResponse']
|
|
596
452
|
|
|
@@ -604,7 +460,7 @@ async function autoDeriveExecutionAccounts({
|
|
|
604
460
|
// Update token index
|
|
605
461
|
tokenIndex += response.accountsToSave.length
|
|
606
462
|
|
|
607
|
-
|
|
463
|
+
logger.debug('After stage', stage, 'tokenIndices', tokenIndices, 'nextTokenIndex', tokenIndex)
|
|
608
464
|
|
|
609
465
|
// Collect the derived accounts
|
|
610
466
|
for (const meta of response.accountsToSave) {
|
|
@@ -633,9 +489,9 @@ async function autoDeriveExecutionAccounts({
|
|
|
633
489
|
stage = response.nextStage
|
|
634
490
|
}
|
|
635
491
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
492
|
+
logger.debug('Resulting derived accounts:', derivedAccounts)
|
|
493
|
+
logger.debug('Resulting derived address lookup tables:', lookupTables)
|
|
494
|
+
logger.debug('Resulting derived token indexes:', tokenIndices)
|
|
639
495
|
|
|
640
496
|
return {
|
|
641
497
|
accounts: derivedAccounts,
|
package/src/solana/hasher.ts
CHANGED
|
@@ -10,10 +10,11 @@ import {
|
|
|
10
10
|
toUtf8Bytes,
|
|
11
11
|
zeroPadValue,
|
|
12
12
|
} from 'ethers'
|
|
13
|
+
import type { ReadonlyDeep } from 'type-fest'
|
|
13
14
|
|
|
14
15
|
import { decodeExtraArgs } from '../extra-args.ts'
|
|
15
16
|
import type { LeafHasher } from '../hasher/index.ts'
|
|
16
|
-
import { type CCIPMessage, type
|
|
17
|
+
import { type CCIPMessage, type Lane, type WithLogger, CCIPVersion } from '../types.ts'
|
|
17
18
|
import { getAddressBytes, getDataBytes, networkInfo, toLeArray } from '../utils.ts'
|
|
18
19
|
|
|
19
20
|
const SvmExtraArgsSchema = {
|
|
@@ -37,11 +38,19 @@ const SvmTokenAmountsSchema = {
|
|
|
37
38
|
},
|
|
38
39
|
} as const
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Creates a leaf hasher for Solana v1.6 CCIP messages.
|
|
43
|
+
* @param lane - Lane configuration.
|
|
44
|
+
* @returns Leaf hasher function for Solana messages.
|
|
45
|
+
*/
|
|
46
|
+
export function getV16SolanaLeafHasher(
|
|
47
|
+
lane: Lane,
|
|
48
|
+
{ logger = console }: WithLogger = {},
|
|
49
|
+
): LeafHasher<typeof CCIPVersion.V1_6> {
|
|
41
50
|
if (lane.version !== CCIPVersion.V1_6)
|
|
42
51
|
throw new Error(`Unsupported lane version: ${lane.version}`)
|
|
43
52
|
|
|
44
|
-
return (message:
|
|
53
|
+
return (message: ReadonlyDeep<CCIPMessage<typeof CCIPVersion.V1_6>>): string => {
|
|
45
54
|
let parsedArgs
|
|
46
55
|
if ('accountIsWritableBitmap' in message) {
|
|
47
56
|
parsedArgs = {
|
|
@@ -95,7 +104,7 @@ export function getV16SolanaLeafHasher(lane: Lane): LeafHasher<typeof CCIPVersio
|
|
|
95
104
|
...[receiver].filter((a) => hexlify(a) !== ZeroHash),
|
|
96
105
|
...parsedArgs.accounts.map((a) => zeroPadValue(bs58.decode(a), 32)),
|
|
97
106
|
]
|
|
98
|
-
|
|
107
|
+
logger.debug(
|
|
99
108
|
'v1.6 solana leafHasher',
|
|
100
109
|
packedValues.map((o) => (o instanceof Uint8Array ? `[${o.length}]:${hexlify(o)}` : o)),
|
|
101
110
|
)
|