@chainlink/ccip-sdk 1.1.0 → 1.2.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/dist/api/index.d.ts +165 -15
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +236 -61
- package/dist/api/index.js.map +1 -1
- package/dist/api/types.d.ts +119 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +5 -5
- package/dist/aptos/index.js.map +1 -1
- package/dist/chain.d.ts +65 -24
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +84 -11
- package/dist/chain.js.map +1 -1
- package/dist/errors/codes.d.ts +1 -0
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/errors/codes.js +1 -0
- package/dist/errors/codes.js.map +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/recovery.d.ts.map +1 -1
- package/dist/errors/recovery.js +1 -0
- package/dist/errors/recovery.js.map +1 -1
- package/dist/errors/specialized.d.ts +21 -0
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +31 -1
- package/dist/errors/specialized.js.map +1 -1
- package/dist/evm/abi/OffRamp_2_0.d.ts +18 -17
- package/dist/evm/abi/OffRamp_2_0.d.ts.map +1 -1
- package/dist/evm/abi/OffRamp_2_0.js +19 -21
- package/dist/evm/abi/OffRamp_2_0.js.map +1 -1
- package/dist/evm/abi/TokenPool_2_0.d.ts +0 -4
- package/dist/evm/abi/TokenPool_2_0.d.ts.map +1 -1
- package/dist/evm/abi/TokenPool_2_0.js +0 -1
- package/dist/evm/abi/TokenPool_2_0.js.map +1 -1
- package/dist/evm/gas.d.ts +14 -4
- package/dist/evm/gas.d.ts.map +1 -1
- package/dist/evm/gas.js +7 -6
- package/dist/evm/gas.js.map +1 -1
- package/dist/evm/index.d.ts +39 -8
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +106 -36
- package/dist/evm/index.js.map +1 -1
- package/dist/extra-args.d.ts +18 -8
- package/dist/extra-args.d.ts.map +1 -1
- package/dist/extra-args.js +6 -6
- package/dist/extra-args.js.map +1 -1
- package/dist/gas.d.ts +1 -1
- package/dist/gas.d.ts.map +1 -1
- package/dist/gas.js +7 -2
- package/dist/gas.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/requests.d.ts +11 -5
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +4 -7
- package/dist/requests.js.map +1 -1
- package/dist/solana/index.d.ts +2 -2
- package/dist/solana/index.d.ts.map +1 -1
- package/dist/solana/index.js +3 -2
- package/dist/solana/index.js.map +1 -1
- package/dist/solana/utils.js +2 -2
- package/dist/solana/utils.js.map +1 -1
- package/dist/sui/exec.d.ts +30 -0
- package/dist/sui/exec.d.ts.map +1 -0
- package/dist/sui/exec.js +92 -0
- package/dist/sui/exec.js.map +1 -0
- package/dist/sui/index.d.ts +7 -2
- package/dist/sui/index.d.ts.map +1 -1
- package/dist/sui/index.js +23 -65
- package/dist/sui/index.js.map +1 -1
- package/dist/sui/manuallyExec/index.d.ts.map +1 -1
- package/dist/sui/manuallyExec/index.js +10 -13
- package/dist/sui/manuallyExec/index.js.map +1 -1
- package/dist/sui/objects.d.ts.map +1 -1
- package/dist/sui/objects.js +4 -2
- package/dist/sui/objects.js.map +1 -1
- package/dist/sui/types.d.ts +9 -1
- package/dist/sui/types.d.ts.map +1 -1
- package/dist/sui/types.js.map +1 -1
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +34 -26
- package/dist/ton/index.js.map +1 -1
- package/dist/utils.d.ts +10 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +10 -4
- package/dist/utils.js.map +1 -1
- package/package.json +7 -7
- package/src/api/index.ts +271 -59
- package/src/api/types.ts +126 -1
- package/src/aptos/index.ts +7 -9
- package/src/chain.ts +136 -38
- package/src/errors/codes.ts +1 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recovery.ts +2 -0
- package/src/errors/specialized.ts +33 -1
- package/src/evm/abi/OffRamp_2_0.ts +19 -21
- package/src/evm/abi/TokenPool_2_0.ts +0 -1
- package/src/evm/gas.ts +18 -20
- package/src/evm/index.ts +126 -34
- package/src/extra-args.ts +18 -8
- package/src/gas.ts +8 -3
- package/src/index.ts +5 -0
- package/src/requests.ts +18 -12
- package/src/solana/index.ts +3 -2
- package/src/solana/utils.ts +2 -2
- package/src/sui/exec.ts +131 -0
- package/src/sui/index.ts +33 -98
- package/src/sui/manuallyExec/index.ts +11 -17
- package/src/sui/objects.ts +4 -2
- package/src/sui/types.ts +10 -1
- package/src/ton/index.ts +47 -26
- package/src/utils.ts +10 -4
package/src/evm/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
JsonRpcProvider,
|
|
13
13
|
WebSocketProvider,
|
|
14
14
|
ZeroAddress,
|
|
15
|
+
formatUnits,
|
|
15
16
|
getAddress,
|
|
16
17
|
hexlify,
|
|
17
18
|
isBytesLike,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
isHexString,
|
|
20
21
|
keccak256,
|
|
21
22
|
toBeHex,
|
|
23
|
+
toBigInt,
|
|
22
24
|
zeroPadValue,
|
|
23
25
|
} from 'ethers'
|
|
24
26
|
import type { TypedContract } from 'ethers-abitype'
|
|
@@ -37,7 +39,6 @@ import {
|
|
|
37
39
|
} from '../chain.ts'
|
|
38
40
|
import {
|
|
39
41
|
CCIPAddressInvalidEvmError,
|
|
40
|
-
CCIPApiClientNotAvailableError,
|
|
41
42
|
CCIPBlockNotFoundError,
|
|
42
43
|
CCIPContractNotRouterError,
|
|
43
44
|
CCIPContractTypeInvalidError,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
CCIPHasherVersionUnsupportedError,
|
|
49
50
|
CCIPLogDataInvalidError,
|
|
50
51
|
CCIPSourceChainUnsupportedError,
|
|
52
|
+
CCIPTokenDecimalsInsufficientError,
|
|
51
53
|
CCIPTokenNotConfiguredError,
|
|
52
54
|
CCIPTokenPoolChainConfigNotFoundError,
|
|
53
55
|
CCIPTransactionNotFoundError,
|
|
@@ -67,7 +69,6 @@ import {
|
|
|
67
69
|
type ChainLog,
|
|
68
70
|
type ChainTransaction,
|
|
69
71
|
type CommitReport,
|
|
70
|
-
type ExecutionInput,
|
|
71
72
|
type ExecutionReceipt,
|
|
72
73
|
type ExecutionState,
|
|
73
74
|
type Lane,
|
|
@@ -694,7 +695,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
694
695
|
offRampABI = OffRamp_1_6_ABI
|
|
695
696
|
// falls through
|
|
696
697
|
case CCIPVersion.V2_0: {
|
|
697
|
-
offRampABI
|
|
698
|
+
offRampABI ??= OffRamp_2_0_ABI
|
|
698
699
|
const contract = new Contract(
|
|
699
700
|
offRamp,
|
|
700
701
|
offRampABI,
|
|
@@ -1005,9 +1006,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1005
1006
|
}
|
|
1006
1007
|
|
|
1007
1008
|
/**
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1010
|
-
*
|
|
1009
|
+
* Generates unsigned EVM transactions for sending a CCIP message.
|
|
1010
|
+
*
|
|
1011
|
+
* @param opts - Send message options with sender address for populating transaction fields.
|
|
1012
|
+
* @returns Unsigned EVM transaction set containing 0 or more token approval txs
|
|
1013
|
+
* (if needed at the time of generation), followed by a ccipSend TransactionRequest.
|
|
1014
|
+
*
|
|
1015
|
+
* @remarks
|
|
1016
|
+
* When a token in `tokenAmounts` has `ZeroAddress` as its address, the corresponding
|
|
1017
|
+
* amount is included as native `value` in the `ccipSend` transaction instead of
|
|
1018
|
+
* going through the ERC-20 approve flow.
|
|
1011
1019
|
*/
|
|
1012
1020
|
async generateUnsignedSendMessage(
|
|
1013
1021
|
opts: Parameters<Chain['generateUnsignedSendMessage']>[0],
|
|
@@ -1140,13 +1148,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1140
1148
|
async generateUnsignedExecute(
|
|
1141
1149
|
opts: Parameters<Chain['generateUnsignedExecute']>[0],
|
|
1142
1150
|
): Promise<UnsignedEVMTx> {
|
|
1143
|
-
|
|
1144
|
-
if (!('input' in opts)) {
|
|
1145
|
-
if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
|
|
1146
|
-
;({ offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId))
|
|
1147
|
-
} else {
|
|
1148
|
-
;({ offRamp, input } = opts)
|
|
1149
|
-
}
|
|
1151
|
+
const { offRamp, input, gasLimit } = await this.resolveExecuteOpts(opts)
|
|
1150
1152
|
if ('verifications' in input) {
|
|
1151
1153
|
const contract = new Contract(
|
|
1152
1154
|
offRamp,
|
|
@@ -1174,14 +1176,14 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1174
1176
|
messageId,
|
|
1175
1177
|
input.verifications.map(({ destAddress }) => destAddress),
|
|
1176
1178
|
input.verifications.map(({ ccvData }) => hexlify(ccvData)),
|
|
1177
|
-
BigInt(
|
|
1179
|
+
BigInt(gasLimit ?? 0),
|
|
1178
1180
|
{ from: offRamp }, // internal method
|
|
1179
1181
|
)
|
|
1180
1182
|
const execTx = await contract.execute.populateTransaction(
|
|
1181
1183
|
input.encodedMessage,
|
|
1182
1184
|
input.verifications.map(({ destAddress }) => destAddress),
|
|
1183
1185
|
input.verifications.map(({ ccvData }) => hexlify(ccvData)),
|
|
1184
|
-
BigInt(
|
|
1186
|
+
BigInt(gasLimit ?? 0),
|
|
1185
1187
|
)
|
|
1186
1188
|
execTx.gasLimit = txGasLimit + 40000n // plus `execute`'s overhead
|
|
1187
1189
|
return { family: ChainFamily.EVM, transactions: [execTx] }
|
|
@@ -1198,7 +1200,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1198
1200
|
interfaces.EVM2EVMOffRamp_v1_2,
|
|
1199
1201
|
this.provider,
|
|
1200
1202
|
) as unknown as TypedContract<typeof EVM2EVMOffRamp_1_2_ABI>
|
|
1201
|
-
const gasOverride = BigInt(
|
|
1203
|
+
const gasOverride = BigInt(gasLimit ?? 0)
|
|
1202
1204
|
manualExecTx = await contract.manuallyExecute.populateTransaction(
|
|
1203
1205
|
{
|
|
1204
1206
|
...input,
|
|
@@ -1225,9 +1227,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1225
1227
|
},
|
|
1226
1228
|
[
|
|
1227
1229
|
{
|
|
1228
|
-
receiverExecutionGasLimit: BigInt(
|
|
1230
|
+
receiverExecutionGasLimit: BigInt(gasLimit ?? 0),
|
|
1229
1231
|
tokenGasOverrides: input.message.tokenAmounts.map(() =>
|
|
1230
|
-
BigInt(opts.tokensGasLimit ??
|
|
1232
|
+
BigInt(opts.tokensGasLimit ?? gasLimit ?? 0),
|
|
1231
1233
|
),
|
|
1232
1234
|
},
|
|
1233
1235
|
],
|
|
@@ -1280,9 +1282,9 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1280
1282
|
[
|
|
1281
1283
|
[
|
|
1282
1284
|
{
|
|
1283
|
-
receiverExecutionGasLimit: BigInt(
|
|
1285
|
+
receiverExecutionGasLimit: BigInt(gasLimit ?? 0),
|
|
1284
1286
|
tokenGasOverrides: input.message.tokenAmounts.map(() =>
|
|
1285
|
-
BigInt(opts.tokensGasLimit ??
|
|
1287
|
+
BigInt(opts.tokensGasLimit ?? gasLimit ?? 0),
|
|
1286
1288
|
),
|
|
1287
1289
|
},
|
|
1288
1290
|
],
|
|
@@ -1296,27 +1298,27 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1296
1298
|
|
|
1297
1299
|
/* Executing a message for the first time has some hard try/catches on-chain
|
|
1298
1300
|
* so we need to ensure some lower-bounds gasLimits */
|
|
1299
|
-
let
|
|
1301
|
+
let txGasLimit = await this.provider.estimateGas(manualExecTx)
|
|
1300
1302
|
if (
|
|
1301
1303
|
'gasLimit' in input.message &&
|
|
1302
1304
|
input.message.gasLimit &&
|
|
1303
|
-
|
|
1305
|
+
txGasLimit < input.message.gasLimit + 100000n
|
|
1304
1306
|
)
|
|
1305
1307
|
// if message requested gasLimit, ensure execution more than 100k above requested, otherwise it's clearly a try/catch fail
|
|
1306
|
-
|
|
1307
|
-
else if ('gasLimit' in input.message && !input.message.gasLimit &&
|
|
1308
|
+
txGasLimit = BigInt(input.message.gasLimit) + 200000n
|
|
1309
|
+
else if ('gasLimit' in input.message && !input.message.gasLimit && txGasLimit < 240000n)
|
|
1308
1310
|
// if message didn't request gasLimit, ensure execution gasLimit is above 240k (empiric)
|
|
1309
|
-
|
|
1310
|
-
manualExecTx.gasLimit =
|
|
1311
|
+
txGasLimit = 240000n
|
|
1312
|
+
manualExecTx.gasLimit = txGasLimit
|
|
1311
1313
|
|
|
1312
1314
|
return { family: ChainFamily.EVM, transactions: [manualExecTx] }
|
|
1313
1315
|
}
|
|
1314
1316
|
|
|
1315
1317
|
/**
|
|
1316
1318
|
* {@inheritDoc Chain.execute}
|
|
1317
|
-
* @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer
|
|
1318
|
-
* @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
|
|
1319
|
-
* @throws {@link CCIPExecTxRevertedError} if execution transaction reverts
|
|
1319
|
+
* @throws {@link CCIPWalletInvalidError} if wallet is not a valid Signer.
|
|
1320
|
+
* @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm.
|
|
1321
|
+
* @throws {@link CCIPExecTxRevertedError} if execution transaction reverts.
|
|
1320
1322
|
*/
|
|
1321
1323
|
async execute(opts: Parameters<Chain['execute']>[0]) {
|
|
1322
1324
|
const wallet = opts.wallet
|
|
@@ -1407,7 +1409,16 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1407
1409
|
}
|
|
1408
1410
|
}
|
|
1409
1411
|
|
|
1410
|
-
/**
|
|
1412
|
+
/**
|
|
1413
|
+
* Fetches the token pool configuration for an EVM token pool contract.
|
|
1414
|
+
*
|
|
1415
|
+
* @param tokenPool - Token pool contract address.
|
|
1416
|
+
* @returns Token pool config containing token, router, typeAndVersion, and optionally minBlockConfirmations.
|
|
1417
|
+
*
|
|
1418
|
+
* @remarks
|
|
1419
|
+
* For pools with version \>= 2.0, also returns `minBlockConfirmations` for
|
|
1420
|
+
* Faster-Than-Finality (FTF) support. Pre-2.0 pools omit this field.
|
|
1421
|
+
*/
|
|
1411
1422
|
async getTokenPoolConfig(tokenPool: string): Promise<{
|
|
1412
1423
|
token: string
|
|
1413
1424
|
router: string
|
|
@@ -1452,7 +1463,22 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1452
1463
|
)
|
|
1453
1464
|
}
|
|
1454
1465
|
|
|
1455
|
-
/**
|
|
1466
|
+
/**
|
|
1467
|
+
* Fetches remote chain configurations for an EVM token pool contract.
|
|
1468
|
+
*
|
|
1469
|
+
* @param tokenPool - Token pool address on the current chain.
|
|
1470
|
+
* @param remoteChainSelector - Optional chain selector to filter results to a single destination.
|
|
1471
|
+
* @returns Record mapping chain names to {@link TokenPoolRemote} configs.
|
|
1472
|
+
*
|
|
1473
|
+
* @remarks
|
|
1474
|
+
* Handles 3 pool version branches:
|
|
1475
|
+
* - v1.5: single remote pool via `getRemotePool`, standard rate limiters.
|
|
1476
|
+
* - v1.6: multiple remote pools via `getRemotePools`, standard rate limiters.
|
|
1477
|
+
* - v2.0+: multiple remote pools plus FTF (Faster-Than-Finality) rate limiters
|
|
1478
|
+
* (`customBlockConfirmationsOutboundRateLimiterState` / `customBlockConfirmationsInboundRateLimiterState`).
|
|
1479
|
+
*
|
|
1480
|
+
* @throws {@link CCIPTokenPoolChainConfigNotFoundError} if remote token is not configured for a chain.
|
|
1481
|
+
*/
|
|
1456
1482
|
async getTokenPoolRemotes(
|
|
1457
1483
|
tokenPool: string,
|
|
1458
1484
|
remoteChainSelector?: bigint,
|
|
@@ -1737,10 +1763,76 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1737
1763
|
override async estimateReceiveExecution(
|
|
1738
1764
|
opts: Parameters<NonNullable<Chain['estimateReceiveExecution']>>[0],
|
|
1739
1765
|
): Promise<number> {
|
|
1766
|
+
const convertAmounts = (
|
|
1767
|
+
tokenAmounts?: readonly ((
|
|
1768
|
+
| { token: string }
|
|
1769
|
+
| { destTokenAddress: string; extraData?: string }
|
|
1770
|
+
) & {
|
|
1771
|
+
amount: bigint
|
|
1772
|
+
})[],
|
|
1773
|
+
) =>
|
|
1774
|
+
!tokenAmounts
|
|
1775
|
+
? undefined
|
|
1776
|
+
: Promise.all(
|
|
1777
|
+
tokenAmounts.map(async (ta) => {
|
|
1778
|
+
if (!('destTokenAddress' in ta)) return ta
|
|
1779
|
+
let amount = ta.amount
|
|
1780
|
+
if (isHexString(ta.extraData, 32)) {
|
|
1781
|
+
// extraData is source token decimals in most pools derived from standard TP contracts;
|
|
1782
|
+
// we can identify for it being exactly 32B and being a small integer; otherwise, assume same decimals
|
|
1783
|
+
const sourceDecimals = toBigInt(ta.extraData)
|
|
1784
|
+
if (0 < sourceDecimals && sourceDecimals <= 36) {
|
|
1785
|
+
const { decimals: destDecimals } = await this.getTokenInfo(ta.destTokenAddress)
|
|
1786
|
+
amount =
|
|
1787
|
+
(amount * BigInt(10) ** BigInt(destDecimals)) /
|
|
1788
|
+
BigInt(10) ** BigInt(sourceDecimals)
|
|
1789
|
+
if (amount === 0n)
|
|
1790
|
+
throw new CCIPTokenDecimalsInsufficientError(
|
|
1791
|
+
ta.destTokenAddress,
|
|
1792
|
+
destDecimals,
|
|
1793
|
+
this.network.name,
|
|
1794
|
+
formatUnits(amount, sourceDecimals),
|
|
1795
|
+
)
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
return { token: ta.destTokenAddress, amount }
|
|
1799
|
+
}),
|
|
1800
|
+
)
|
|
1801
|
+
|
|
1802
|
+
let opts_
|
|
1803
|
+
if (!('offRamp' in opts)) {
|
|
1804
|
+
const { lane, message, metadata } = await this.getMessageById(opts.messageId)
|
|
1805
|
+
|
|
1806
|
+
const offRamp =
|
|
1807
|
+
('offRampAddress' in message && message.offRampAddress) ||
|
|
1808
|
+
metadata?.offRamp ||
|
|
1809
|
+
(await this.apiClient!.getExecutionInput(opts.messageId)).offRamp
|
|
1810
|
+
|
|
1811
|
+
opts_ = {
|
|
1812
|
+
offRamp,
|
|
1813
|
+
message: {
|
|
1814
|
+
sourceChainSelector: lane.sourceChainSelector,
|
|
1815
|
+
messageId: message.messageId,
|
|
1816
|
+
receiver: message.receiver,
|
|
1817
|
+
sender: message.sender,
|
|
1818
|
+
data: message.data,
|
|
1819
|
+
destTokenAmounts: await convertAmounts(message.tokenAmounts),
|
|
1820
|
+
},
|
|
1821
|
+
}
|
|
1822
|
+
} else {
|
|
1823
|
+
opts_ = {
|
|
1824
|
+
...opts,
|
|
1825
|
+
message: {
|
|
1826
|
+
...opts.message,
|
|
1827
|
+
destTokenAmounts: await convertAmounts(opts.message.destTokenAmounts),
|
|
1828
|
+
},
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1740
1832
|
const destRouter = await this.getRouterForOffRamp(
|
|
1741
|
-
|
|
1742
|
-
|
|
1833
|
+
opts_.offRamp,
|
|
1834
|
+
opts_.message.sourceChainSelector,
|
|
1743
1835
|
)
|
|
1744
|
-
return estimateExecGas({ provider: this.provider, router: destRouter, ...
|
|
1836
|
+
return estimateExecGas({ provider: this.provider, router: destRouter, ...opts_ })
|
|
1745
1837
|
}
|
|
1746
1838
|
}
|
package/src/extra-args.ts
CHANGED
|
@@ -68,7 +68,7 @@ export type EVMExtraArgsV2 = EVMExtraArgsV1 & {
|
|
|
68
68
|
export type GenericExtraArgsV3 = {
|
|
69
69
|
/** Gas limit for execution on the destination chain (uint32). */
|
|
70
70
|
gasLimit: bigint
|
|
71
|
-
/** Number of block confirmations
|
|
71
|
+
/** Number of source-chain block confirmations to wait before relaying the message. */
|
|
72
72
|
blockConfirmations: number
|
|
73
73
|
/** Cross-chain verifier addresses (EVM addresses). */
|
|
74
74
|
ccvs: string[]
|
|
@@ -132,7 +132,17 @@ export type SuiExtraArgsV1 = EVMExtraArgsV2 & {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* Union
|
|
135
|
+
* Union of all supported extra arguments formats for CCIP messages.
|
|
136
|
+
*
|
|
137
|
+
* The SDK auto-detects the correct variant based on the fields provided:
|
|
138
|
+
* - {@link EVMExtraArgsV1} - EVM legacy (gasLimit only)
|
|
139
|
+
* - {@link EVMExtraArgsV2} - EVM with out-of-order execution support
|
|
140
|
+
* - {@link GenericExtraArgsV3} - Generic V3 with minimum block confirmations, cross-chain verifiers, custom executor, and per-token receiver/args
|
|
141
|
+
* - {@link SVMExtraArgsV1} - Solana (compute units, accounts)
|
|
142
|
+
* - {@link SuiExtraArgsV1} - Sui (gas limit, receiver object IDs)
|
|
143
|
+
*
|
|
144
|
+
* @see {@link encodeExtraArgs} - Encode extra arguments for on-chain use.
|
|
145
|
+
* @see {@link decodeExtraArgs} - Decode extra arguments from bytes.
|
|
136
146
|
*/
|
|
137
147
|
export type ExtraArgs =
|
|
138
148
|
| EVMExtraArgsV1
|
|
@@ -146,10 +156,10 @@ export type ExtraArgs =
|
|
|
146
156
|
* The args are *to* a dest network, but are encoded as a message *from* this source chain.
|
|
147
157
|
* E.g. Solana uses Borsh to encode extraArgs in its produced requests, even those targeting EVM.
|
|
148
158
|
*
|
|
149
|
-
* @param args - Extra arguments to encode
|
|
150
|
-
* @param from - Source chain family for encoding format (defaults to EVM)
|
|
151
|
-
* @returns Encoded extra arguments as hex string
|
|
152
|
-
* @throws {@link CCIPChainFamilyUnsupportedError} if chain family not supported
|
|
159
|
+
* @param args - Extra arguments to encode.
|
|
160
|
+
* @param from - Source chain family for encoding format (defaults to EVM).
|
|
161
|
+
* @returns Encoded extra arguments as hex string.
|
|
162
|
+
* @throws {@link CCIPChainFamilyUnsupportedError} if chain family not supported.
|
|
153
163
|
*
|
|
154
164
|
* @example
|
|
155
165
|
* ```typescript
|
|
@@ -175,8 +185,8 @@ export function encodeExtraArgs(args: ExtraArgs, from: ChainFamily = ChainFamily
|
|
|
175
185
|
* @param data - Extra arguments bytearray data.
|
|
176
186
|
* @param from - Optional chain family to narrow decoding attempts.
|
|
177
187
|
* @returns Extra arguments object if found, undefined otherwise.
|
|
178
|
-
* @throws {@link CCIPChainFamilyUnsupportedError} if specified chain family not supported
|
|
179
|
-
* @throws {@link CCIPExtraArgsParseError} if data cannot be parsed as valid extra args
|
|
188
|
+
* @throws {@link CCIPChainFamilyUnsupportedError} if specified chain family not supported.
|
|
189
|
+
* @throws {@link CCIPExtraArgsParseError} if data cannot be parsed as valid extra args.
|
|
180
190
|
*
|
|
181
191
|
* @example
|
|
182
192
|
* ```typescript
|
package/src/gas.ts
CHANGED
|
@@ -28,7 +28,7 @@ export type EstimateMessageInput = {
|
|
|
28
28
|
data?: BytesLike
|
|
29
29
|
/**
|
|
30
30
|
* optional tokenAmounts; `amount` with either source `token` (as in MessageInput) or
|
|
31
|
-
* `{ sourceTokenAddress?, sourcePoolAddress, destTokenAddress }` (as in v1.5..
|
|
31
|
+
* `{ sourceTokenAddress?, sourcePoolAddress, destTokenAddress }` (as in v1.5..v2.0 tokenAmounts)
|
|
32
32
|
* can be provided
|
|
33
33
|
*/
|
|
34
34
|
tokenAmounts?: readonly ({
|
|
@@ -122,7 +122,12 @@ export async function estimateReceiveExecution({
|
|
|
122
122
|
const tokenAmount =
|
|
123
123
|
'destTokenAddress' in ta
|
|
124
124
|
? ta
|
|
125
|
-
: await sourceToDestTokenAddresses(
|
|
125
|
+
: await sourceToDestTokenAddresses({
|
|
126
|
+
source,
|
|
127
|
+
onRamp,
|
|
128
|
+
destChainSelector: dest.network.chainSelector,
|
|
129
|
+
sourceTokenAmount: ta,
|
|
130
|
+
})
|
|
126
131
|
const sourceTokenAddress =
|
|
127
132
|
'token' in ta
|
|
128
133
|
? ta.token
|
|
@@ -147,10 +152,10 @@ export async function estimateReceiveExecution({
|
|
|
147
152
|
}),
|
|
148
153
|
)
|
|
149
154
|
return dest.estimateReceiveExecution({
|
|
150
|
-
receiver: message.receiver,
|
|
151
155
|
offRamp,
|
|
152
156
|
message: {
|
|
153
157
|
messageId: message.messageId ?? hexlify(randomBytes(32)),
|
|
158
|
+
receiver: message.receiver,
|
|
154
159
|
sender: message.sender,
|
|
155
160
|
data: message.data,
|
|
156
161
|
sourceChainSelector: source.network.chainSelector,
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,9 @@ export type {
|
|
|
12
12
|
APIErrorResponse,
|
|
13
13
|
CCIPAPIClientContext,
|
|
14
14
|
LaneLatencyResponse,
|
|
15
|
+
MessageSearchFilters,
|
|
16
|
+
MessageSearchPage,
|
|
17
|
+
MessageSearchResult,
|
|
15
18
|
} from './api/index.ts'
|
|
16
19
|
export {
|
|
17
20
|
CCIPAPIClient,
|
|
@@ -41,6 +44,7 @@ export {
|
|
|
41
44
|
type EVMExtraArgsV1,
|
|
42
45
|
type EVMExtraArgsV2,
|
|
43
46
|
type ExtraArgs,
|
|
47
|
+
type GenericExtraArgsV3,
|
|
44
48
|
type SVMExtraArgsV1,
|
|
45
49
|
type SuiExtraArgsV1,
|
|
46
50
|
decodeExtraArgs,
|
|
@@ -100,6 +104,7 @@ export type { UnsignedEVMTx } from './evm/index.ts'
|
|
|
100
104
|
import { SolanaChain } from './solana/index.ts'
|
|
101
105
|
export type { UnsignedSolanaTx } from './solana/index.ts'
|
|
102
106
|
import { SuiChain } from './sui/index.ts'
|
|
107
|
+
export type { UnsignedSuiTx } from './sui/index.ts'
|
|
103
108
|
import { TONChain } from './ton/index.ts'
|
|
104
109
|
export type { UnsignedTONTx } from './ton/index.ts'
|
|
105
110
|
import { ChainFamily, NetworkType } from './types.ts'
|
package/src/requests.ts
CHANGED
|
@@ -89,9 +89,9 @@ function decodeJsonMessage(data: Record<string, unknown> | undefined) {
|
|
|
89
89
|
k?.match(/(selector|amount|nonce|number|limit|bitmap|juels)$/i)
|
|
90
90
|
? BigInt(v as string | number | bigint)
|
|
91
91
|
: k?.match(/(^dest.*address)|(receiver|offramp|accounts)/i)
|
|
92
|
-
? decodeAddress(v as BytesLike, destFamily)
|
|
92
|
+
? decodeAddress((typeof v === 'bigint' ? v.toString() : v) as BytesLike, destFamily)
|
|
93
93
|
: k?.match(/((source.*address)|sender|issuer|origin|onramp|(feetoken$)|(token.*address$))/i)
|
|
94
|
-
? decodeAddress(v as BytesLike, sourceFamily)
|
|
94
|
+
? decodeAddress((typeof v === 'bigint' ? v.toString() : v) as BytesLike, sourceFamily)
|
|
95
95
|
: v instanceof Uint8Array ||
|
|
96
96
|
(Array.isArray(v) && v.length >= 4 && v.every((e) => typeof e === 'number'))
|
|
97
97
|
? hexlify(getDataBytes(v as readonly number[]))
|
|
@@ -456,10 +456,7 @@ export async function* getMessagesForSender(
|
|
|
456
456
|
* Resolves token routing by querying the TokenAdminRegistry and TokenPool
|
|
457
457
|
* to find the corresponding destination chain token.
|
|
458
458
|
*
|
|
459
|
-
* @param
|
|
460
|
-
* @param destChainSelector - Destination chain selector
|
|
461
|
-
* @param onRamp - OnRamp contract address
|
|
462
|
-
* @param sourceTokenAmount - Token amount object containing `token` and `amount`
|
|
459
|
+
* @param opts - options to convert source to dest token addresses
|
|
463
460
|
* @returns Extended token amount with `sourcePoolAddress`, `sourceTokenAddress`, and `destTokenAddress`
|
|
464
461
|
*
|
|
465
462
|
* @throws {@link CCIPTokenNotInRegistryError} if token is not registered in TokenAdminRegistry
|
|
@@ -479,12 +476,21 @@ export async function* getMessagesForSender(
|
|
|
479
476
|
* console.log(`Dest token: ${tokenAmount.destTokenAddress}`)
|
|
480
477
|
* ```
|
|
481
478
|
*/
|
|
482
|
-
export async function sourceToDestTokenAddresses<S extends { token: string }>(
|
|
483
|
-
source
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
sourceTokenAmount
|
|
487
|
-
|
|
479
|
+
export async function sourceToDestTokenAddresses<S extends { token: string }>({
|
|
480
|
+
source,
|
|
481
|
+
onRamp,
|
|
482
|
+
destChainSelector,
|
|
483
|
+
sourceTokenAmount,
|
|
484
|
+
}: {
|
|
485
|
+
/** Source chain instance */
|
|
486
|
+
source: Chain
|
|
487
|
+
/** OnRamp contract address */
|
|
488
|
+
onRamp: string
|
|
489
|
+
/** Destination chain selector */
|
|
490
|
+
destChainSelector: bigint
|
|
491
|
+
/** Token amount object containing `token` and `amount` */
|
|
492
|
+
sourceTokenAmount: S
|
|
493
|
+
}): Promise<
|
|
488
494
|
S & {
|
|
489
495
|
sourcePoolAddress: string
|
|
490
496
|
sourceTokenAddress: string
|
package/src/solana/index.ts
CHANGED
|
@@ -1118,9 +1118,10 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
|
|
|
1118
1118
|
payer,
|
|
1119
1119
|
...opts
|
|
1120
1120
|
}: Parameters<Chain['generateUnsignedExecute']>[0]): Promise<UnsignedSolanaTx> {
|
|
1121
|
-
|
|
1121
|
+
const resolved = await this.resolveExecuteOpts(opts)
|
|
1122
|
+
if (!('message' in resolved.input) || !('computeUnits' in resolved.input.message))
|
|
1122
1123
|
throw new CCIPExecutionReportChainMismatchError('Solana')
|
|
1123
|
-
const { offRamp, input } =
|
|
1124
|
+
const { offRamp, input } = resolved
|
|
1124
1125
|
const execReport_ = input as ExecutionInput<CCIPMessage_V1_6_Solana>
|
|
1125
1126
|
return generateUnsignedExecuteReport(
|
|
1126
1127
|
this,
|
package/src/solana/utils.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
CCIPTransactionNotFinalizedError,
|
|
28
28
|
} from '../errors/index.ts'
|
|
29
29
|
import type { ChainLog, WithLogger } from '../types.ts'
|
|
30
|
-
import { getDataBytes, sleep } from '../utils.ts'
|
|
30
|
+
import { bigIntReplacer, getDataBytes, sleep } from '../utils.ts'
|
|
31
31
|
import type { IDL as BASE_TOKEN_POOL_IDL } from './idl/1.6.0/BASE_TOKEN_POOL.ts'
|
|
32
32
|
import type { UnsignedSolanaTx, Wallet } from './types.ts'
|
|
33
33
|
import type { RateLimiterState } from '../chain.ts'
|
|
@@ -347,7 +347,7 @@ export async function simulateTransaction(
|
|
|
347
347
|
throw new SendTransactionError({
|
|
348
348
|
action: 'simulate',
|
|
349
349
|
signature: '',
|
|
350
|
-
transactionMessage: JSON.stringify(result.value.err),
|
|
350
|
+
transactionMessage: JSON.stringify(result.value.err, bigIntReplacer),
|
|
351
351
|
logs: result.value.logs!,
|
|
352
352
|
})
|
|
353
353
|
}
|
package/src/sui/exec.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { SuiClient } from '@mysten/sui/client'
|
|
2
|
+
import type { Keypair } from '@mysten/sui/cryptography'
|
|
3
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CCIPError,
|
|
7
|
+
CCIPErrorCode,
|
|
8
|
+
CCIPExecTxRevertedError,
|
|
9
|
+
CCIPExecutionReportChainMismatchError,
|
|
10
|
+
} from '../errors/index.ts'
|
|
11
|
+
import { type ExecutionInput, ChainFamily } from '../types.ts'
|
|
12
|
+
import { getCcipStateAddress } from './discovery.ts'
|
|
13
|
+
import {
|
|
14
|
+
type SuiManuallyExecuteInput,
|
|
15
|
+
type TokenConfig,
|
|
16
|
+
buildManualExecutionPTB,
|
|
17
|
+
} from './manuallyExec/index.ts'
|
|
18
|
+
import { fetchTokenConfigs, getObjectRef, getReceiverModule } from './objects.ts'
|
|
19
|
+
import type { CCIPMessage_V1_6_Sui, UnsignedSuiTx } from './types.ts'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Builds a Sui manual-execution PTB and returns it as an {@link UnsignedSuiTx}.
|
|
23
|
+
*
|
|
24
|
+
* @param client - Sui RPC client.
|
|
25
|
+
* @param offRamp - OffRamp object ID / address.
|
|
26
|
+
* @param input - Execution input (message + proofs).
|
|
27
|
+
* @param opts - Optional overrides such as `gasLimit` and `receiverObjectIds`.
|
|
28
|
+
* @returns Serialized unsigned transaction ready to sign and submit.
|
|
29
|
+
*/
|
|
30
|
+
export async function generateUnsignedExecutePTB(
|
|
31
|
+
client: SuiClient,
|
|
32
|
+
offRamp: string,
|
|
33
|
+
input: ExecutionInput<CCIPMessage_V1_6_Sui>,
|
|
34
|
+
opts?: { gasLimit?: number | bigint; receiverObjectIds?: string[] },
|
|
35
|
+
): Promise<UnsignedSuiTx> {
|
|
36
|
+
if (!('message' in input)) {
|
|
37
|
+
throw new CCIPExecutionReportChainMismatchError('Sui')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ccip = await getCcipStateAddress(offRamp, client)
|
|
41
|
+
|
|
42
|
+
const ccipObjectRef = await getObjectRef(ccip, client)
|
|
43
|
+
const [offrampStateObject, receiverConfig] = await Promise.all([
|
|
44
|
+
getObjectRef(offRamp, client),
|
|
45
|
+
getReceiverModule(client, ccip, ccipObjectRef, input.message.receiver),
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
let tokenConfigs: TokenConfig[] = []
|
|
49
|
+
if (input.message.tokenAmounts.length !== 0) {
|
|
50
|
+
tokenConfigs = await fetchTokenConfigs(client, ccip, ccipObjectRef, input.message.tokenAmounts)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const suiInput: SuiManuallyExecuteInput = {
|
|
54
|
+
executionReport: input,
|
|
55
|
+
offrampAddress: offRamp,
|
|
56
|
+
ccipAddress: ccip,
|
|
57
|
+
ccipObjectRef,
|
|
58
|
+
offrampStateObject,
|
|
59
|
+
receiverConfig,
|
|
60
|
+
tokenConfigs,
|
|
61
|
+
...(opts?.receiverObjectIds ? { overrideReceiverObjectIds: opts.receiverObjectIds } : {}),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const tx = buildManualExecutionPTB(suiInput)
|
|
65
|
+
|
|
66
|
+
if (opts?.gasLimit) {
|
|
67
|
+
tx.setGasBudget(opts.gasLimit)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
family: ChainFamily.Sui,
|
|
72
|
+
transactions: [tx.serialize()],
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Signs and executes a pre-built {@link UnsignedSuiTx} using the provided keypair.
|
|
78
|
+
*
|
|
79
|
+
* @param client - Sui RPC client.
|
|
80
|
+
* @param wallet - Keypair used to sign the transaction.
|
|
81
|
+
* @param unsignedTx - The unsigned Sui transaction to execute.
|
|
82
|
+
* @param logger - Optional logger.
|
|
83
|
+
* @returns The finalized transaction digest string.
|
|
84
|
+
*/
|
|
85
|
+
export async function signAndExecuteSuiTx(
|
|
86
|
+
client: SuiClient,
|
|
87
|
+
wallet: Keypair,
|
|
88
|
+
unsignedTx: UnsignedSuiTx,
|
|
89
|
+
logger?: { info: (...args: unknown[]) => void },
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
const tx = Transaction.from(unsignedTx.transactions[0])
|
|
92
|
+
|
|
93
|
+
logger?.info('Executing Sui CCIP execute transaction...')
|
|
94
|
+
|
|
95
|
+
let digest: string
|
|
96
|
+
try {
|
|
97
|
+
const result = await client.signAndExecuteTransaction({
|
|
98
|
+
signer: wallet,
|
|
99
|
+
transaction: tx,
|
|
100
|
+
options: {
|
|
101
|
+
showEffects: true,
|
|
102
|
+
showEvents: true,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (result.effects?.status.status !== 'success') {
|
|
107
|
+
const errorMsg = result.effects?.status.error ?? 'Unknown error'
|
|
108
|
+
throw new CCIPExecTxRevertedError(result.digest, { context: { error: errorMsg } })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
digest = result.digest
|
|
112
|
+
} catch (e) {
|
|
113
|
+
if (e instanceof CCIPExecTxRevertedError) throw e
|
|
114
|
+
throw new CCIPError(
|
|
115
|
+
CCIPErrorCode.TRANSACTION_NOT_FINALIZED,
|
|
116
|
+
`Failed to send Sui execute transaction: ${(e as Error).message}`,
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
logger?.info(`Waiting for Sui transaction ${digest} to be finalized...`)
|
|
121
|
+
|
|
122
|
+
await client.waitForTransaction({
|
|
123
|
+
digest,
|
|
124
|
+
options: {
|
|
125
|
+
showEffects: true,
|
|
126
|
+
showEvents: true,
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
return digest
|
|
131
|
+
}
|