@chainlink/ccip-sdk 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/chain.d.ts +53 -27
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +72 -17
- 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 +114 -28
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/types.d.ts +1 -1
- package/dist/evm/types.d.ts.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 +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +6 -6
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +11 -5
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +6 -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 +1 -1
- 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/index.d.ts.map +1 -1
- package/dist/sui/index.js +1 -1
- package/dist/sui/index.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 +9 -9
- package/src/api/index.ts +271 -59
- package/src/api/types.ts +126 -1
- package/src/chain.ts +121 -43
- 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 +141 -28
- package/src/evm/types.ts +1 -1
- package/src/extra-args.ts +18 -8
- package/src/gas.ts +8 -3
- package/src/index.ts +4 -0
- package/src/offchain.ts +6 -8
- package/src/requests.ts +19 -12
- package/src/solana/index.ts +3 -1
- package/src/solana/utils.ts +2 -2
- package/src/sui/index.ts +3 -1
- package/src/ton/index.ts +47 -26
- package/src/utils.ts +10 -4
package/src/chain.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BytesLike, dataLength } from 'ethers'
|
|
1
|
+
import { type BytesLike, dataLength, keccak256 } from 'ethers'
|
|
2
2
|
import type { PickDeep, SetOptional } from 'type-fest'
|
|
3
3
|
|
|
4
4
|
import { type LaneLatencyResponse, CCIPAPIClient } from './api/index.ts'
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
SuiExtraArgsV1,
|
|
25
25
|
} from './extra-args.ts'
|
|
26
26
|
import type { LeafHasher } from './hasher/common.ts'
|
|
27
|
+
import { decodeMessageV1 } from './messages.ts'
|
|
27
28
|
import { getOffchainTokenData } from './offchain.ts'
|
|
28
29
|
import { getMessagesInTx } from './requests.ts'
|
|
29
30
|
import { DEFAULT_GAS_LIMIT } from './shared/constants.ts'
|
|
@@ -68,13 +69,13 @@ const V3_FIELDS = new Set([
|
|
|
68
69
|
'tokenArgs',
|
|
69
70
|
])
|
|
70
71
|
|
|
71
|
-
/** Throw if any key in extraArgs is not in the allowed set. */
|
|
72
|
+
/** Throw {@link CCIPArgumentInvalidError} if any key in extraArgs is not in the allowed set. */
|
|
72
73
|
function assertNoUnknownFields(
|
|
73
74
|
extraArgs: Partial<ExtraArgs>,
|
|
74
75
|
allowed: Set<string>,
|
|
75
76
|
variant: string,
|
|
76
77
|
): void {
|
|
77
|
-
const unknown = Object.keys(extraArgs).filter((k) => !allowed.has(k))
|
|
78
|
+
const unknown = Object.keys(extraArgs).filter((k) => k !== '_tag' && !allowed.has(k))
|
|
78
79
|
if (unknown.length)
|
|
79
80
|
throw new CCIPArgumentInvalidError(
|
|
80
81
|
'extraArgs',
|
|
@@ -201,7 +202,7 @@ export type TokenInfo = {
|
|
|
201
202
|
*/
|
|
202
203
|
export const LaneFeature = {
|
|
203
204
|
/**
|
|
204
|
-
* Minimum block confirmations for Faster
|
|
205
|
+
* Minimum block confirmations for Faster-Than-Finality (FTF).
|
|
205
206
|
* - **absent**: the lane does not support FTF (pre-v2.0 lane).
|
|
206
207
|
* - **0**: the lane supports FTF, but it is not enabled for this
|
|
207
208
|
* token (e.g. the token pool predates FTF, or FTF is configured
|
|
@@ -285,6 +286,11 @@ export type RateLimiterState = {
|
|
|
285
286
|
* @remarks
|
|
286
287
|
* Each entry represents the configuration needed to transfer tokens
|
|
287
288
|
* from the current chain to a specific destination chain.
|
|
289
|
+
*
|
|
290
|
+
* The `customBlockConfirmationsOutboundRateLimiterState` and
|
|
291
|
+
* `customBlockConfirmationsInboundRateLimiterState` fields are present only for
|
|
292
|
+
* TokenPool v2.0+ contracts. These provide separate rate limits applied when
|
|
293
|
+
* Faster-Than-Finality (FTF) custom block confirmations are used.
|
|
288
294
|
*/
|
|
289
295
|
export type TokenPoolRemote = {
|
|
290
296
|
/** Address of the remote token on the destination chain. */
|
|
@@ -333,7 +339,7 @@ export type TokenPoolConfig = {
|
|
|
333
339
|
*/
|
|
334
340
|
typeAndVersion?: string
|
|
335
341
|
/**
|
|
336
|
-
* Min custom block confirmations for Faster
|
|
342
|
+
* Min custom block confirmations for Faster-Than-Finality (FTF),
|
|
337
343
|
* if TokenPool version \>= v2.0.0 and FTF is supported on this lane.
|
|
338
344
|
* `0` indicates FTF is supported but not enabled for this token; `>0` indicates FTF is enabled
|
|
339
345
|
* with this many minimum confirmations.
|
|
@@ -396,7 +402,7 @@ export type ExecuteOpts = (
|
|
|
396
402
|
| {
|
|
397
403
|
/**
|
|
398
404
|
* messageId of message to execute; requires `apiClient`.
|
|
399
|
-
*
|
|
405
|
+
* The SDK will fetch execution inputs (offRamp, proofs/verifications) from the CCIP API.
|
|
400
406
|
*/
|
|
401
407
|
messageId: string
|
|
402
408
|
}
|
|
@@ -1019,16 +1025,62 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1019
1025
|
* When `opts` already contains `input` the method is a no-op and returns it unchanged.
|
|
1020
1026
|
* When `opts` contains only a `messageId` it calls `apiClient.getExecutionInput` and merges
|
|
1021
1027
|
* the result back with any extra opts fields (e.g. `gasLimit`).
|
|
1028
|
+
* If opts.gasLimit is undefined and `estimateReceiveExecution` is available, try to estimate gasLimitOverride
|
|
1022
1029
|
*
|
|
1023
1030
|
* @throws {@link CCIPApiClientNotAvailableError} if `messageId` is provided but no apiClient
|
|
1024
1031
|
*/
|
|
1025
1032
|
protected async resolveExecuteOpts(
|
|
1026
1033
|
opts: ExecuteOpts,
|
|
1027
|
-
): Promise<
|
|
1028
|
-
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
|
|
1034
|
+
): Promise<Extract<ExecuteOpts, { input: unknown }>> {
|
|
1035
|
+
let opts_: Extract<typeof opts, { input: unknown }>
|
|
1036
|
+
if ('input' in opts) {
|
|
1037
|
+
opts_ = opts
|
|
1038
|
+
} else if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
|
|
1039
|
+
else {
|
|
1040
|
+
const { offRamp, ...input } = await this.apiClient.getExecutionInput(opts.messageId)
|
|
1041
|
+
opts_ = { ...opts, offRamp, input }
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (
|
|
1045
|
+
opts_.gasLimit == null &&
|
|
1046
|
+
this.estimateReceiveExecution &&
|
|
1047
|
+
(!('message' in opts_.input) ||
|
|
1048
|
+
!opts_.input.message.tokenAmounts.length ||
|
|
1049
|
+
opts_.input.message.tokenAmounts.every((ta) => 'destTokenAddress' in ta))
|
|
1050
|
+
) {
|
|
1051
|
+
let message
|
|
1052
|
+
if ('message' in opts_.input) {
|
|
1053
|
+
message = {
|
|
1054
|
+
...opts_.input.message,
|
|
1055
|
+
// pass `tokenAmount` with `destTokenAddress` to estimate
|
|
1056
|
+
destTokenAmounts: opts_.input.message.tokenAmounts,
|
|
1057
|
+
}
|
|
1058
|
+
} else {
|
|
1059
|
+
const decoded = decodeMessageV1(opts_.input.encodedMessage)
|
|
1060
|
+
message = {
|
|
1061
|
+
...decoded,
|
|
1062
|
+
messageId: keccak256(opts_.input.encodedMessage),
|
|
1063
|
+
destTokenAmounts: decoded.tokenTransfer,
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
const estimated = await this.estimateReceiveExecution({
|
|
1068
|
+
offRamp: opts_.offRamp,
|
|
1069
|
+
message,
|
|
1070
|
+
})
|
|
1071
|
+
this.logger.debug('Estimated receiver execution:', estimated)
|
|
1072
|
+
if (
|
|
1073
|
+
('gasLimit' in message && estimated > message.gasLimit) ||
|
|
1074
|
+
('ccipReceiveGasLimit' in message && estimated > message.ccipReceiveGasLimit)
|
|
1075
|
+
)
|
|
1076
|
+
opts_.gasLimit = estimated
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
// ignore if receiver fails, let estimation of execute method itself throw if needed
|
|
1079
|
+
this.logger.debug('Failed to auto-estimateReceiveExecution for:', opts, err)
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
return opts_
|
|
1032
1084
|
}
|
|
1033
1085
|
|
|
1034
1086
|
/**
|
|
@@ -1056,21 +1108,24 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1056
1108
|
/**
|
|
1057
1109
|
* Execute messages in report in an offRamp.
|
|
1058
1110
|
*
|
|
1059
|
-
* @param opts - {@link ExecuteOpts} with chain-specific wallet to sign and send tx
|
|
1060
|
-
* @returns Promise resolving to transaction of the execution
|
|
1111
|
+
* @param opts - {@link ExecuteOpts} with chain-specific wallet to sign and send tx.
|
|
1112
|
+
* @returns Promise resolving to transaction of the execution.
|
|
1061
1113
|
*
|
|
1062
|
-
* @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer
|
|
1063
|
-
* @throws {@link
|
|
1064
|
-
* @throws {@link
|
|
1114
|
+
* @throws {@link CCIPWalletNotSignerError} if wallet is not a valid signer.
|
|
1115
|
+
* @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm.
|
|
1116
|
+
* @throws {@link CCIPExecTxRevertedError} if execution transaction reverts.
|
|
1117
|
+
* @throws {@link CCIPMerkleRootMismatchError} if merkle proof is invalid.
|
|
1118
|
+
*
|
|
1119
|
+
* @example Manual execution using message ID (simplified, requires API)
|
|
1120
|
+
* ```typescript
|
|
1121
|
+
* const receipt = await dest.execute({ messageId: '0x...', wallet })
|
|
1122
|
+
* ```
|
|
1065
1123
|
*
|
|
1066
|
-
* @example Manual execution
|
|
1124
|
+
* @example Manual execution using transaction hash
|
|
1067
1125
|
* ```typescript
|
|
1068
1126
|
* const input = await source.getExecutionInput({ request, verifications })
|
|
1069
1127
|
* const receipt = await dest.execute({ offRamp, input, wallet })
|
|
1070
|
-
* console.log(`Executed: ${receipt.log.transactionHash}`)
|
|
1071
1128
|
* ```
|
|
1072
|
-
* @throws {@link CCIPWalletNotSignerError} if wallet cannot sign transactions
|
|
1073
|
-
* @throws {@link CCIPExecTxNotConfirmedError} if execution transaction fails to confirm
|
|
1074
1129
|
*/
|
|
1075
1130
|
abstract execute(
|
|
1076
1131
|
opts: ExecuteOpts & {
|
|
@@ -1118,10 +1173,10 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1118
1173
|
* Uses this chain's selector as the source.
|
|
1119
1174
|
*
|
|
1120
1175
|
* @param destChainSelector - Destination CCIP chain selector (bigint)
|
|
1176
|
+
* @param numberOfBlocks - Optional number of block confirmations to use for latency
|
|
1177
|
+
* calculation. When omitted or 0, uses the lane's default finality. When provided
|
|
1178
|
+
* as a positive integer, the API returns latency for that custom finality value.
|
|
1121
1179
|
* @returns Promise resolving to {@link LaneLatencyResponse} containing:
|
|
1122
|
-
* - `lane.sourceNetworkInfo` - Source chain metadata (name, selector, chainId)
|
|
1123
|
-
* - `lane.destNetworkInfo` - Destination chain metadata
|
|
1124
|
-
* - `lane.routerAddress` - Router contract address on source chain
|
|
1125
1180
|
* - `totalMs` - Estimated delivery time in milliseconds
|
|
1126
1181
|
*
|
|
1127
1182
|
* @throws {@link CCIPApiClientNotAvailableError} if apiClient was disabled (set to `null`)
|
|
@@ -1137,19 +1192,31 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1137
1192
|
* try {
|
|
1138
1193
|
* const latency = await chain.getLaneLatency(4949039107694359620n) // Arbitrum
|
|
1139
1194
|
* console.log(`Estimated delivery: ${Math.round(latency.totalMs / 60000)} minutes`)
|
|
1140
|
-
* console.log(`Router: ${latency.lane.routerAddress}`)
|
|
1141
1195
|
* } catch (err) {
|
|
1142
1196
|
* if (err instanceof CCIPHttpError) {
|
|
1143
1197
|
* console.error(`API error: ${err.context.apiErrorCode}`)
|
|
1144
1198
|
* }
|
|
1145
1199
|
* }
|
|
1146
1200
|
* ```
|
|
1201
|
+
*
|
|
1202
|
+
* @example Get latency with custom block confirmations
|
|
1203
|
+
* ```typescript
|
|
1204
|
+
* const latency = await chain.getLaneLatency(4949039107694359620n, 10)
|
|
1205
|
+
* console.log(`Latency with 10 confirmations: ${Math.round(latency.totalMs / 60000)} minutes`)
|
|
1206
|
+
* ```
|
|
1147
1207
|
*/
|
|
1148
|
-
async getLaneLatency(
|
|
1208
|
+
async getLaneLatency(
|
|
1209
|
+
destChainSelector: bigint,
|
|
1210
|
+
numberOfBlocks?: number,
|
|
1211
|
+
): Promise<LaneLatencyResponse> {
|
|
1149
1212
|
if (!this.apiClient) {
|
|
1150
1213
|
throw new CCIPApiClientNotAvailableError()
|
|
1151
1214
|
}
|
|
1152
|
-
return this.apiClient.getLaneLatency(
|
|
1215
|
+
return this.apiClient.getLaneLatency(
|
|
1216
|
+
this.network.chainSelector,
|
|
1217
|
+
destChainSelector,
|
|
1218
|
+
numberOfBlocks,
|
|
1219
|
+
)
|
|
1153
1220
|
}
|
|
1154
1221
|
|
|
1155
1222
|
/**
|
|
@@ -1438,8 +1505,15 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1438
1505
|
* Returns a copy of a message, populating missing fields like `extraArgs` with defaults.
|
|
1439
1506
|
* It's expected to return a message suitable at least for basic token transfers.
|
|
1440
1507
|
*
|
|
1441
|
-
* @param message - AnyMessage (from source), containing at least `receiver
|
|
1442
|
-
* @returns A message suitable for `sendMessage` to this destination chain family
|
|
1508
|
+
* @param message - AnyMessage (from source), containing at least `receiver`.
|
|
1509
|
+
* @returns A message suitable for `sendMessage` to this destination chain family.
|
|
1510
|
+
*
|
|
1511
|
+
* @remarks
|
|
1512
|
+
* V3 (GenericExtraArgsV3) is auto-detected when any V3-only field is present
|
|
1513
|
+
* (e.g. `blockConfirmations`, `ccvs`, `ccvArgs`, `executor`, `executorArgs`,
|
|
1514
|
+
* `tokenReceiver`, `tokenArgs`). Otherwise defaults to V2 (EVMExtraArgsV2).
|
|
1515
|
+
*
|
|
1516
|
+
* @throws {@link CCIPArgumentInvalidError} if extraArgs contains unknown fields for the detected version.
|
|
1443
1517
|
*/
|
|
1444
1518
|
static buildMessageForDest(
|
|
1445
1519
|
message: Parameters<ChainStatic['buildMessageForDest']>[0],
|
|
@@ -1481,23 +1555,27 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
|
|
|
1481
1555
|
|
|
1482
1556
|
/**
|
|
1483
1557
|
* Estimate `ccipReceive` execution cost (gas, computeUnits) for this *dest*
|
|
1484
|
-
* @param opts - estimation options
|
|
1558
|
+
* @param opts - estimation options, either `messageId` (for api) or `offRamp`, `message` (with `destTokenAmounts`)
|
|
1485
1559
|
* @returns estimated execution cost (gas or computeUnits)
|
|
1486
1560
|
*/
|
|
1487
|
-
estimateReceiveExecution?(
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1561
|
+
estimateReceiveExecution?(
|
|
1562
|
+
opts:
|
|
1563
|
+
| {
|
|
1564
|
+
offRamp: string
|
|
1565
|
+
message: {
|
|
1566
|
+
sourceChainSelector: bigint
|
|
1567
|
+
messageId: string
|
|
1568
|
+
receiver: string
|
|
1569
|
+
sender?: string
|
|
1570
|
+
data?: BytesLike
|
|
1571
|
+
destTokenAmounts?: readonly ((
|
|
1572
|
+
| { token: string }
|
|
1573
|
+
| { destTokenAddress: string; extraData?: string }
|
|
1574
|
+
) & { amount: bigint })[]
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
| { messageId: string },
|
|
1578
|
+
): Promise<number>
|
|
1501
1579
|
}
|
|
1502
1580
|
|
|
1503
1581
|
/**
|
package/src/errors/codes.ts
CHANGED
package/src/errors/index.ts
CHANGED
|
@@ -156,6 +156,7 @@ export { CCIPBorshMethodUnknownError, CCIPBorshTypeUnknownError } from './specia
|
|
|
156
156
|
|
|
157
157
|
// Specialized errors - HTTP & Data
|
|
158
158
|
export {
|
|
159
|
+
CCIPAbortError,
|
|
159
160
|
CCIPBlockBeforeTimestampNotFoundError,
|
|
160
161
|
CCIPDataFormatUnsupportedError,
|
|
161
162
|
CCIPDataParseError,
|
package/src/errors/recovery.ts
CHANGED
|
@@ -170,6 +170,8 @@ export const DEFAULT_RECOVERY_HINTS: Partial<Record<CCIPErrorCode, string>> = {
|
|
|
170
170
|
RPC_NOT_FOUND: 'No RPC endpoint found. Configure an RPC URL.',
|
|
171
171
|
TIMEOUT:
|
|
172
172
|
'Request timed out. Check network connectivity and try again. Consider increasing timeoutMs if the server is slow.',
|
|
173
|
+
ABORT:
|
|
174
|
+
'Request was aborted. This is usually intentional (e.g. user cancellation or component unmount).',
|
|
173
175
|
|
|
174
176
|
VIEM_ADAPTER_ERROR:
|
|
175
177
|
'Check that your viem client has both account and chain defined. For WalletClient, use createWalletClient({ chain, account, ... }).',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CCIPErrorOptions, CCIPError } from './CCIPError.ts'
|
|
2
2
|
import { CCIPErrorCode } from './codes.ts'
|
|
3
3
|
import { isTransientHttpStatus } from '../http-status.ts'
|
|
4
|
+
import { bigIntReplacer } from '../utils.ts'
|
|
4
5
|
|
|
5
6
|
// Chain/Network
|
|
6
7
|
|
|
@@ -166,7 +167,10 @@ export class CCIPMessageInvalidError extends CCIPError {
|
|
|
166
167
|
override readonly name = 'CCIPMessageInvalidError'
|
|
167
168
|
/** Creates a message invalid error. */
|
|
168
169
|
constructor(data: unknown, options?: CCIPErrorOptions) {
|
|
169
|
-
const dataStr =
|
|
170
|
+
const dataStr =
|
|
171
|
+
typeof data === 'object' && data !== null
|
|
172
|
+
? JSON.stringify(data, bigIntReplacer)
|
|
173
|
+
: String(data)
|
|
170
174
|
super(CCIPErrorCode.MESSAGE_INVALID, `Invalid CCIP message format: ${dataStr}`, {
|
|
171
175
|
...options,
|
|
172
176
|
isTransient: false,
|
|
@@ -1203,6 +1207,34 @@ export class CCIPTimeoutError extends CCIPError {
|
|
|
1203
1207
|
}
|
|
1204
1208
|
}
|
|
1205
1209
|
|
|
1210
|
+
/**
|
|
1211
|
+
* Thrown when a request is aborted via an AbortSignal.
|
|
1212
|
+
*
|
|
1213
|
+
* @example
|
|
1214
|
+
* ```typescript
|
|
1215
|
+
* const controller = new AbortController()
|
|
1216
|
+
* setTimeout(() => controller.abort(), 1000)
|
|
1217
|
+
* try {
|
|
1218
|
+
* await api.searchMessages({ sender: '0x...' }, { signal: controller.signal })
|
|
1219
|
+
* } catch (error) {
|
|
1220
|
+
* if (error instanceof CCIPAbortError) {
|
|
1221
|
+
* console.log(`Request was cancelled: ${error.context.operation}`)
|
|
1222
|
+
* }
|
|
1223
|
+
* }
|
|
1224
|
+
* ```
|
|
1225
|
+
*/
|
|
1226
|
+
export class CCIPAbortError extends CCIPError {
|
|
1227
|
+
override readonly name = 'CCIPAbortError'
|
|
1228
|
+
/** Creates an abort error. */
|
|
1229
|
+
constructor(operation: string, options?: CCIPErrorOptions) {
|
|
1230
|
+
super(CCIPErrorCode.ABORT, `Request aborted: ${operation}`, {
|
|
1231
|
+
...options,
|
|
1232
|
+
isTransient: false,
|
|
1233
|
+
context: { ...options?.context, operation },
|
|
1234
|
+
})
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1206
1238
|
/**
|
|
1207
1239
|
* Thrown for not implemented features.
|
|
1208
1240
|
*
|
|
@@ -222,9 +222,13 @@ export default [
|
|
|
222
222
|
name: 'getAllSourceChainConfigs',
|
|
223
223
|
inputs: [],
|
|
224
224
|
outputs: [
|
|
225
|
-
{ name: '', type: 'uint64[]', internalType: 'uint64[]' },
|
|
226
225
|
{
|
|
227
|
-
name: '',
|
|
226
|
+
name: 'sourceChainSelectors',
|
|
227
|
+
type: 'uint64[]',
|
|
228
|
+
internalType: 'uint64[]',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'sourceChainConfigs',
|
|
228
232
|
type: 'tuple[]',
|
|
229
233
|
internalType: 'struct OffRamp.SourceChainConfig[]',
|
|
230
234
|
components: [
|
|
@@ -418,25 +422,6 @@ export default [
|
|
|
418
422
|
],
|
|
419
423
|
anonymous: false,
|
|
420
424
|
},
|
|
421
|
-
{
|
|
422
|
-
type: 'event',
|
|
423
|
-
name: 'MaxGasBufferToUpdateStateUpdated',
|
|
424
|
-
inputs: [
|
|
425
|
-
{
|
|
426
|
-
name: 'oldMaxGasBufferToUpdateState',
|
|
427
|
-
type: 'uint32',
|
|
428
|
-
indexed: false,
|
|
429
|
-
internalType: 'uint32',
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: 'newMaxGasBufferToUpdateState',
|
|
433
|
-
type: 'uint32',
|
|
434
|
-
indexed: false,
|
|
435
|
-
internalType: 'uint32',
|
|
436
|
-
},
|
|
437
|
-
],
|
|
438
|
-
anonymous: false,
|
|
439
|
-
},
|
|
440
425
|
{
|
|
441
426
|
type: 'event',
|
|
442
427
|
name: 'OwnershipTransferRequested',
|
|
@@ -611,6 +596,19 @@ export default [
|
|
|
611
596
|
name: 'InvalidEncodingVersion',
|
|
612
597
|
inputs: [{ name: 'version', type: 'uint8', internalType: 'uint8' }],
|
|
613
598
|
},
|
|
599
|
+
{
|
|
600
|
+
type: 'error',
|
|
601
|
+
name: 'InvalidFinalityForReceiver',
|
|
602
|
+
inputs: [
|
|
603
|
+
{ name: 'receiver', type: 'address', internalType: 'address' },
|
|
604
|
+
{ name: 'msgBlockDepth', type: 'uint16', internalType: 'uint16' },
|
|
605
|
+
{
|
|
606
|
+
name: 'requiredBlockDepth',
|
|
607
|
+
type: 'uint16',
|
|
608
|
+
internalType: 'uint16',
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
},
|
|
614
612
|
{
|
|
615
613
|
type: 'error',
|
|
616
614
|
name: 'InvalidGasLimitOverride',
|
|
@@ -1546,7 +1546,6 @@ export default [
|
|
|
1546
1546
|
name: 'InvalidTransferFeeBps',
|
|
1547
1547
|
inputs: [{ name: 'bps', type: 'uint256', internalType: 'uint256' }],
|
|
1548
1548
|
},
|
|
1549
|
-
{ type: 'error', name: 'MismatchedArrayLengths', inputs: [] },
|
|
1550
1549
|
{ type: 'error', name: 'MustBeProposedOwner', inputs: [] },
|
|
1551
1550
|
{
|
|
1552
1551
|
type: 'error',
|
package/src/evm/gas.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type BytesLike,
|
|
2
3
|
type JsonRpcApiProvider,
|
|
3
4
|
Contract,
|
|
4
5
|
FunctionFragment,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
import type { TypedContract } from 'ethers-abitype'
|
|
15
16
|
import { memoize } from 'micro-memoize'
|
|
16
17
|
|
|
17
|
-
import type { Chain } from '../chain.ts'
|
|
18
18
|
import TokenABI from './abi/BurnMintERC677Token.ts'
|
|
19
19
|
import RouterABI from './abi/Router.ts'
|
|
20
20
|
import { defaultAbiCoder, interfaces } from './const.ts'
|
|
@@ -77,13 +77,17 @@ const findBalancesSlot = memoize(
|
|
|
77
77
|
{ maxArgs: 1 },
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
-
type EstimateExecGasOpts =
|
|
81
|
-
Parameters<NonNullable<Chain['estimateReceiveExecution']>>[0],
|
|
82
|
-
'message' | 'receiver'
|
|
83
|
-
> & {
|
|
84
|
-
/* */
|
|
80
|
+
type EstimateExecGasOpts = {
|
|
85
81
|
provider: JsonRpcApiProvider
|
|
86
82
|
router: string
|
|
83
|
+
message: {
|
|
84
|
+
sourceChainSelector: bigint
|
|
85
|
+
messageId: string
|
|
86
|
+
receiver: string
|
|
87
|
+
sender?: string
|
|
88
|
+
data?: BytesLike
|
|
89
|
+
destTokenAmounts?: readonly { token: string; amount: bigint }[]
|
|
90
|
+
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/**
|
|
@@ -91,12 +95,7 @@ type EstimateExecGasOpts = Pick<
|
|
|
91
95
|
* @param opts - Options for estimation: provider, destRouter, receiver address and message
|
|
92
96
|
* @returns Estimated gasLimit
|
|
93
97
|
*/
|
|
94
|
-
export async function estimateExecGas({
|
|
95
|
-
provider,
|
|
96
|
-
router,
|
|
97
|
-
receiver,
|
|
98
|
-
message,
|
|
99
|
-
}: EstimateExecGasOpts) {
|
|
98
|
+
export async function estimateExecGas({ provider, router, message }: EstimateExecGasOpts) {
|
|
100
99
|
// we need to override the state, increasing receiver's balance for each token, to simulate the
|
|
101
100
|
// state after tokens were transferred by the offRamp just before calling `ccipReceive`
|
|
102
101
|
const destAmounts: Record<string, bigint> = {}
|
|
@@ -106,25 +105,24 @@ export async function estimateExecGas({
|
|
|
106
105
|
const tokenContract = new Contract(token, TokenABI, provider) as unknown as TypedContract<
|
|
107
106
|
typeof TokenABI
|
|
108
107
|
>
|
|
109
|
-
const currentBalance = await tokenContract.balanceOf(receiver)
|
|
108
|
+
const currentBalance = await tokenContract.balanceOf(message.receiver)
|
|
110
109
|
destAmounts[token] = currentBalance
|
|
111
110
|
}
|
|
112
111
|
destAmounts[token]! += amount
|
|
113
|
-
const balancesSlot = await findBalancesSlot(token, provider, receiver, router)
|
|
112
|
+
const balancesSlot = await findBalancesSlot(token, provider, message.receiver, router)
|
|
114
113
|
stateOverrides[token] = {
|
|
115
114
|
stateDiff: {
|
|
116
|
-
[solidityPackedKeccak256(['uint256', 'uint256'], [receiver, balancesSlot])]:
|
|
117
|
-
destAmounts[token]!,
|
|
118
|
-
32,
|
|
119
|
-
),
|
|
115
|
+
[solidityPackedKeccak256(['uint256', 'uint256'], [message.receiver, balancesSlot])]:
|
|
116
|
+
toBeHex(destAmounts[token]!, 32),
|
|
120
117
|
},
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
|
|
121
|
+
const senderBytes = getAddressBytes(message.sender ?? '0x')
|
|
124
122
|
const receiverMsg: Any2EVMMessage = {
|
|
125
123
|
...message,
|
|
126
124
|
destTokenAmounts: message.destTokenAmounts ?? [],
|
|
127
|
-
sender:
|
|
125
|
+
sender: senderBytes.length < 32 ? zeroPadValue(senderBytes, 32) : hexlify(senderBytes),
|
|
128
126
|
data: hexlify(getDataBytes(message.data || '0x')),
|
|
129
127
|
sourceChainSelector: message.sourceChainSelector,
|
|
130
128
|
}
|
|
@@ -138,7 +136,7 @@ export async function estimateExecGas({
|
|
|
138
136
|
(await provider.send('eth_estimateGas', [
|
|
139
137
|
{
|
|
140
138
|
from: router,
|
|
141
|
-
to: receiver,
|
|
139
|
+
to: message.receiver,
|
|
142
140
|
data: calldata,
|
|
143
141
|
},
|
|
144
142
|
'latest',
|