@chainlink/ccip-sdk 1.2.1 → 1.3.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 +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +10 -20
- package/dist/api/index.js.map +1 -1
- package/dist/aptos/index.d.ts +2 -2
- package/dist/aptos/index.d.ts.map +1 -1
- package/dist/aptos/index.js +1 -1
- package/dist/aptos/index.js.map +1 -1
- package/dist/chain.d.ts +75 -2
- package/dist/chain.d.ts.map +1 -1
- package/dist/chain.js +19 -0
- 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 +8 -0
- package/dist/errors/specialized.d.ts.map +1 -1
- package/dist/errors/specialized.js +10 -0
- package/dist/errors/specialized.js.map +1 -1
- package/dist/evm/abi/CCTPVerifier_2_0.d.ts +1118 -0
- package/dist/evm/abi/CCTPVerifier_2_0.d.ts.map +1 -0
- package/dist/evm/abi/CCTPVerifier_2_0.js +1147 -0
- package/dist/evm/abi/CCTPVerifier_2_0.js.map +1 -0
- package/dist/evm/abi/USDCTokenPoolProxy_2_0.d.ts +825 -0
- package/dist/evm/abi/USDCTokenPoolProxy_2_0.d.ts.map +1 -0
- package/dist/evm/abi/USDCTokenPoolProxy_2_0.js +873 -0
- package/dist/evm/abi/USDCTokenPoolProxy_2_0.js.map +1 -0
- package/dist/evm/abi/VersionedVerifierResolver_2_0.d.ts +350 -0
- package/dist/evm/abi/VersionedVerifierResolver_2_0.d.ts.map +1 -0
- package/dist/evm/abi/VersionedVerifierResolver_2_0.js +370 -0
- package/dist/evm/abi/VersionedVerifierResolver_2_0.js.map +1 -0
- package/dist/evm/const.d.ts +3 -0
- package/dist/evm/const.d.ts.map +1 -1
- package/dist/evm/const.js +6 -0
- package/dist/evm/const.js.map +1 -1
- package/dist/evm/index.d.ts +24 -3
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +193 -7
- package/dist/evm/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/offchain.d.ts +27 -0
- package/dist/offchain.d.ts.map +1 -1
- package/dist/offchain.js +44 -2
- package/dist/offchain.js.map +1 -1
- package/dist/requests.d.ts +1 -25
- package/dist/requests.d.ts.map +1 -1
- package/dist/requests.js +0 -57
- 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/sui/index.d.ts +2 -2
- 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 +2 -2
- package/dist/ton/index.d.ts.map +1 -1
- package/dist/ton/index.js +28 -49
- package/dist/ton/index.js.map +1 -1
- package/dist/ton/send.d.ts +13 -1
- package/dist/ton/send.d.ts.map +1 -1
- package/dist/ton/send.js +16 -16
- package/dist/ton/send.js.map +1 -1
- package/dist/utils.d.ts +16 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +31 -1
- package/dist/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/api/index.ts +9 -23
- package/src/aptos/index.ts +5 -1
- package/src/chain.ts +85 -2
- 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 +15 -0
- package/src/evm/abi/CCTPVerifier_2_0.ts +1146 -0
- package/src/evm/abi/USDCTokenPoolProxy_2_0.ts +872 -0
- package/src/evm/abi/VersionedVerifierResolver_2_0.ts +369 -0
- package/src/evm/const.ts +6 -0
- package/src/evm/index.ts +262 -8
- package/src/index.ts +6 -2
- package/src/offchain.ts +53 -1
- package/src/requests.ts +1 -59
- package/src/solana/index.ts +5 -1
- package/src/sui/index.ts +2 -1
- package/src/ton/index.ts +41 -56
- package/src/ton/send.ts +20 -21
- package/src/utils.ts +42 -0
package/src/evm/index.ts
CHANGED
|
@@ -34,6 +34,9 @@ import {
|
|
|
34
34
|
type LogFilter,
|
|
35
35
|
type RateLimiterState,
|
|
36
36
|
type TokenPoolRemote,
|
|
37
|
+
type TokenTransferFeeConfig,
|
|
38
|
+
type TokenTransferFeeOpts,
|
|
39
|
+
type TotalFeesEstimate,
|
|
37
40
|
Chain,
|
|
38
41
|
LaneFeature,
|
|
39
42
|
} from '../chain.ts'
|
|
@@ -60,6 +63,7 @@ import {
|
|
|
60
63
|
} from '../errors/index.ts'
|
|
61
64
|
import type { ExtraArgs } from '../extra-args.ts'
|
|
62
65
|
import type { LeafHasher } from '../hasher/common.ts'
|
|
66
|
+
import { CCTP_FINALITY_FAST, getUsdcBurnFees } from '../offchain.ts'
|
|
63
67
|
import { supportedChains } from '../supported-chains.ts'
|
|
64
68
|
import {
|
|
65
69
|
type CCIPExecution,
|
|
@@ -87,6 +91,7 @@ import {
|
|
|
87
91
|
parseTypeAndVersion,
|
|
88
92
|
} from '../utils.ts'
|
|
89
93
|
import type Token_ABI from './abi/BurnMintERC677Token.ts'
|
|
94
|
+
import type CCTPVerifier_2_0_ABI from './abi/CCTPVerifier_2_0.ts'
|
|
90
95
|
import type FeeQuoter_ABI from './abi/FeeQuoter_1_6.ts'
|
|
91
96
|
import type TokenPool_1_5_ABI from './abi/LockReleaseTokenPool_1_5.ts'
|
|
92
97
|
import type TokenPool_ABI from './abi/LockReleaseTokenPool_1_6_1.ts'
|
|
@@ -101,6 +106,8 @@ import type OnRamp_2_0_ABI from './abi/OnRamp_2_0.ts'
|
|
|
101
106
|
import type Router_ABI from './abi/Router.ts'
|
|
102
107
|
import type TokenAdminRegistry_1_5_ABI from './abi/TokenAdminRegistry_1_5.ts'
|
|
103
108
|
import type TokenPool_2_0_ABI from './abi/TokenPool_2_0.ts'
|
|
109
|
+
import type USDCTokenPoolProxy_2_0_ABI from './abi/USDCTokenPoolProxy_2_0.ts'
|
|
110
|
+
import type VersionedVerifierResolver_2_0_ABI from './abi/VersionedVerifierResolver_2_0.ts'
|
|
104
111
|
import {
|
|
105
112
|
CCV_INDEXER_URL,
|
|
106
113
|
VersionedContractABI,
|
|
@@ -241,6 +248,8 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
241
248
|
maxArgs: 1,
|
|
242
249
|
})
|
|
243
250
|
this.getFeeTokens = memoize(this.getFeeTokens.bind(this), { async: true, maxArgs: 1 })
|
|
251
|
+
this.detectUsdcDomains = memoize(this.detectUsdcDomains.bind(this))
|
|
252
|
+
this.resolveVerifier = memoize(this.resolveVerifier.bind(this))
|
|
244
253
|
}
|
|
245
254
|
|
|
246
255
|
/**
|
|
@@ -1018,6 +1027,210 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1018
1027
|
})
|
|
1019
1028
|
}
|
|
1020
1029
|
|
|
1030
|
+
/**
|
|
1031
|
+
* Detect whether a token pool is a USDC/CCTP pool via typeAndVersion, then resolve
|
|
1032
|
+
* the CCTPVerifier address and fetch source/dest CCTP domain IDs.
|
|
1033
|
+
*
|
|
1034
|
+
* @param tokenPool - The token pool address to check.
|
|
1035
|
+
* @param destChainSelector - Destination chain selector for getDomain().
|
|
1036
|
+
* @param ccvs - Cross-chain verifier addresses from extraArgs (fallback for verifier discovery).
|
|
1037
|
+
* @returns Source and dest CCTP domain IDs, or undefined if not a USDC pool.
|
|
1038
|
+
*/
|
|
1039
|
+
private async detectUsdcDomains(
|
|
1040
|
+
tokenPool: string,
|
|
1041
|
+
destChainSelector: bigint,
|
|
1042
|
+
ccvs: string[] = [],
|
|
1043
|
+
): Promise<{ sourceDomain: number; destDomain: number } | undefined> {
|
|
1044
|
+
// 1. Check if pool is USDCTokenPoolProxy
|
|
1045
|
+
let poolType: string
|
|
1046
|
+
try {
|
|
1047
|
+
;[poolType] = await this.typeAndVersion(tokenPool)
|
|
1048
|
+
} catch {
|
|
1049
|
+
return undefined
|
|
1050
|
+
}
|
|
1051
|
+
if (poolType !== 'USDCTokenPoolProxy') return undefined
|
|
1052
|
+
|
|
1053
|
+
// 2. Find CCTPVerifier address
|
|
1054
|
+
let verifierAddress: string | undefined
|
|
1055
|
+
|
|
1056
|
+
// 2a. Try pool's getStaticConfig (returns resolver/verifier address)
|
|
1057
|
+
try {
|
|
1058
|
+
const proxy = new Contract(
|
|
1059
|
+
tokenPool,
|
|
1060
|
+
interfaces.USDCTokenPoolProxy_v2_0,
|
|
1061
|
+
this.provider,
|
|
1062
|
+
) as unknown as TypedContract<typeof USDCTokenPoolProxy_2_0_ABI>
|
|
1063
|
+
const [, , cctpVerifier] = await proxy.getStaticConfig()
|
|
1064
|
+
const candidate = cctpVerifier as string
|
|
1065
|
+
if (candidate && candidate !== ZeroAddress) {
|
|
1066
|
+
verifierAddress = await this.resolveVerifier(candidate, destChainSelector)
|
|
1067
|
+
}
|
|
1068
|
+
} catch {
|
|
1069
|
+
/* proxy may not be initialized */
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// 2b. Fall back to scanning ccvs from extraArgs
|
|
1073
|
+
if (!verifierAddress) {
|
|
1074
|
+
for (const ccv of ccvs) {
|
|
1075
|
+
if (!ccv) continue
|
|
1076
|
+
try {
|
|
1077
|
+
const resolved = await this.resolveVerifier(ccv, destChainSelector)
|
|
1078
|
+
if (resolved) {
|
|
1079
|
+
verifierAddress = resolved
|
|
1080
|
+
break
|
|
1081
|
+
}
|
|
1082
|
+
} catch {
|
|
1083
|
+
/* not a valid contract */
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (!verifierAddress) return undefined
|
|
1089
|
+
|
|
1090
|
+
// 3. Fetch source and dest CCTP domain IDs from verifier
|
|
1091
|
+
try {
|
|
1092
|
+
const verifier = new Contract(
|
|
1093
|
+
verifierAddress,
|
|
1094
|
+
interfaces.CCTPVerifier_v2_0,
|
|
1095
|
+
this.provider,
|
|
1096
|
+
) as unknown as TypedContract<typeof CCTPVerifier_2_0_ABI>
|
|
1097
|
+
const [staticConfig, domainResult] = await Promise.all([
|
|
1098
|
+
verifier.getStaticConfig(),
|
|
1099
|
+
verifier.getDomain(destChainSelector),
|
|
1100
|
+
])
|
|
1101
|
+
return {
|
|
1102
|
+
sourceDomain: Number(staticConfig[3]), // localDomainIdentifier
|
|
1103
|
+
destDomain: Number(domainResult.domainIdentifier),
|
|
1104
|
+
}
|
|
1105
|
+
} catch (err) {
|
|
1106
|
+
if (isError(err, 'CALL_EXCEPTION')) return undefined
|
|
1107
|
+
throw CCIPError.from(err)
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* Given a candidate address, check if it's a CCTPVerifier or VersionedVerifierResolver
|
|
1113
|
+
* and return the actual verifier address (resolving through the resolver if needed).
|
|
1114
|
+
*/
|
|
1115
|
+
private async resolveVerifier(
|
|
1116
|
+
candidate: string,
|
|
1117
|
+
destChainSelector: bigint,
|
|
1118
|
+
): Promise<string | undefined> {
|
|
1119
|
+
try {
|
|
1120
|
+
const [candidateType] = await this.typeAndVersion(candidate)
|
|
1121
|
+
if (candidateType === 'VersionedVerifierResolver') {
|
|
1122
|
+
const resolver = new Contract(
|
|
1123
|
+
candidate,
|
|
1124
|
+
interfaces.VersionedVerifierResolver_v2_0,
|
|
1125
|
+
this.provider,
|
|
1126
|
+
) as unknown as TypedContract<typeof VersionedVerifierResolver_2_0_ABI>
|
|
1127
|
+
return (await resolver.getOutboundImplementation(destChainSelector, '0x')) as string
|
|
1128
|
+
}
|
|
1129
|
+
if (candidateType === 'CCTPVerifier') return candidate
|
|
1130
|
+
} catch {
|
|
1131
|
+
/* not a valid versioned contract */
|
|
1132
|
+
}
|
|
1133
|
+
return undefined
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/** {@inheritDoc Chain.getTotalFeesEstimate} */
|
|
1137
|
+
override async getTotalFeesEstimate(
|
|
1138
|
+
opts: Parameters<Chain['getTotalFeesEstimate']>[0],
|
|
1139
|
+
): Promise<TotalFeesEstimate> {
|
|
1140
|
+
const tokenAmounts = opts.message.tokenAmounts
|
|
1141
|
+
const ccipFee$ = this.getFee(opts)
|
|
1142
|
+
|
|
1143
|
+
if (!tokenAmounts?.length) {
|
|
1144
|
+
return { ccipFee: await ccipFee$ }
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
const { token, amount } = tokenAmounts[0]!
|
|
1148
|
+
|
|
1149
|
+
// Determine blockConfirmations and tokenArgs from extraArgs
|
|
1150
|
+
const extraArgs = opts.message.extraArgs
|
|
1151
|
+
let blockConfirmations = 0
|
|
1152
|
+
let tokenArgs: string = '0x'
|
|
1153
|
+
if (extraArgs && 'blockConfirmations' in extraArgs) {
|
|
1154
|
+
blockConfirmations = extraArgs.blockConfirmations as number
|
|
1155
|
+
tokenArgs = hexlify(extraArgs.tokenArgs as BytesLike)
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Skip pool-level fee lookup for pre-v2.0 lanes
|
|
1159
|
+
const onRamp = await this.getOnRampForRouter(opts.router, opts.destChainSelector)
|
|
1160
|
+
const [, version] = await this.typeAndVersion(onRamp)
|
|
1161
|
+
if (version < CCIPVersion.V2_0) {
|
|
1162
|
+
return { ccipFee: await ccipFee$ }
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
const onRampContract = new Contract(
|
|
1166
|
+
onRamp,
|
|
1167
|
+
interfaces.OnRamp_v2_0,
|
|
1168
|
+
this.provider,
|
|
1169
|
+
) as unknown as TypedContract<typeof OnRamp_2_0_ABI>
|
|
1170
|
+
|
|
1171
|
+
const poolAddress = (await onRampContract.getPoolBySourceToken(
|
|
1172
|
+
opts.destChainSelector,
|
|
1173
|
+
token,
|
|
1174
|
+
)) as string
|
|
1175
|
+
|
|
1176
|
+
const [ccipFee, { tokenTransferFeeConfig }, usdcDomains] = await Promise.all([
|
|
1177
|
+
ccipFee$,
|
|
1178
|
+
this.getTokenPoolConfig(poolAddress, {
|
|
1179
|
+
destChainSelector: opts.destChainSelector,
|
|
1180
|
+
blockConfirmationsRequested: blockConfirmations,
|
|
1181
|
+
tokenArgs,
|
|
1182
|
+
}),
|
|
1183
|
+
this.detectUsdcDomains(
|
|
1184
|
+
poolAddress,
|
|
1185
|
+
opts.destChainSelector,
|
|
1186
|
+
extraArgs && 'ccvs' in extraArgs ? extraArgs.ccvs : [],
|
|
1187
|
+
),
|
|
1188
|
+
])
|
|
1189
|
+
|
|
1190
|
+
// USDC path: use Circle CCTP burn fees
|
|
1191
|
+
if (usdcDomains) {
|
|
1192
|
+
const burnFees = await getUsdcBurnFees(
|
|
1193
|
+
usdcDomains.sourceDomain,
|
|
1194
|
+
usdcDomains.destDomain,
|
|
1195
|
+
this.network.networkType,
|
|
1196
|
+
)
|
|
1197
|
+
const fast = blockConfirmations > 0
|
|
1198
|
+
// Tiers are sorted ascending by finalityThreshold; findLast for fast ensures
|
|
1199
|
+
// we pick the highest tier still within the fast threshold.
|
|
1200
|
+
const tier = fast
|
|
1201
|
+
? burnFees.findLast((t) => t.finalityThreshold <= CCTP_FINALITY_FAST)
|
|
1202
|
+
: burnFees.find((t) => t.finalityThreshold > CCTP_FINALITY_FAST)
|
|
1203
|
+
if (tier && tier.minimumFee > 0) {
|
|
1204
|
+
return {
|
|
1205
|
+
ccipFee,
|
|
1206
|
+
tokenTransferFee: {
|
|
1207
|
+
feeDeducted: (BigInt(amount) * BigInt(tier.minimumFee)) / 10_000n,
|
|
1208
|
+
bps: tier.minimumFee,
|
|
1209
|
+
},
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return { ccipFee }
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Non-USDC path: use on-chain tokenTransferFeeConfig
|
|
1216
|
+
if (!tokenTransferFeeConfig || !tokenTransferFeeConfig.isEnabled) {
|
|
1217
|
+
return { ccipFee }
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const useCustom = blockConfirmations > 0
|
|
1221
|
+
const bps = useCustom
|
|
1222
|
+
? tokenTransferFeeConfig.customBlockConfirmationsTransferFeeBps
|
|
1223
|
+
: tokenTransferFeeConfig.defaultBlockConfirmationsTransferFeeBps
|
|
1224
|
+
|
|
1225
|
+
return {
|
|
1226
|
+
ccipFee,
|
|
1227
|
+
tokenTransferFee: {
|
|
1228
|
+
feeDeducted: (BigInt(amount) * BigInt(bps)) / 10_000n,
|
|
1229
|
+
bps,
|
|
1230
|
+
},
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1021
1234
|
/**
|
|
1022
1235
|
* Generates unsigned EVM transactions for sending a CCIP message.
|
|
1023
1236
|
*
|
|
@@ -1426,44 +1639,84 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1426
1639
|
* Fetches the token pool configuration for an EVM token pool contract.
|
|
1427
1640
|
*
|
|
1428
1641
|
* @param tokenPool - Token pool contract address.
|
|
1429
|
-
* @
|
|
1642
|
+
* @param feeOpts - Optional parameters to also fetch token transfer fee config.
|
|
1643
|
+
* @returns Token pool config containing token, router, typeAndVersion, and optionally
|
|
1644
|
+
* minBlockConfirmations and tokenTransferFeeConfig.
|
|
1430
1645
|
*
|
|
1431
1646
|
* @remarks
|
|
1432
1647
|
* For pools with version \>= 2.0, also returns `minBlockConfirmations` for
|
|
1433
1648
|
* Faster-Than-Finality (FTF) support. Pre-2.0 pools omit this field.
|
|
1649
|
+
* When `feeOpts` is provided and the pool is v2.0+, also fetches token transfer fee config.
|
|
1434
1650
|
*/
|
|
1435
|
-
async getTokenPoolConfig(
|
|
1651
|
+
async getTokenPoolConfig(
|
|
1652
|
+
tokenPool: string,
|
|
1653
|
+
feeOpts?: TokenTransferFeeOpts,
|
|
1654
|
+
): Promise<{
|
|
1436
1655
|
token: string
|
|
1437
1656
|
router: string
|
|
1438
1657
|
typeAndVersion: string
|
|
1439
1658
|
minBlockConfirmations?: number
|
|
1659
|
+
tokenTransferFeeConfig?: TokenTransferFeeConfig
|
|
1440
1660
|
}> {
|
|
1441
1661
|
const [_, version, typeAndVersion] = await this.typeAndVersion(tokenPool)
|
|
1442
1662
|
|
|
1443
|
-
let
|
|
1663
|
+
let token, router, minBlockConfirmations, tokenTransferFeeConfig
|
|
1444
1664
|
if (version < CCIPVersion.V2_0) {
|
|
1445
|
-
contract = new Contract(
|
|
1665
|
+
const contract = new Contract(
|
|
1446
1666
|
tokenPool,
|
|
1447
1667
|
interfaces.TokenPool_v1_6,
|
|
1448
1668
|
this.provider,
|
|
1449
1669
|
) as unknown as TypedContract<typeof TokenPool_ABI>
|
|
1670
|
+
token = contract.getToken()
|
|
1450
1671
|
router = contract.getRouter()
|
|
1451
1672
|
} else {
|
|
1452
|
-
contract = new Contract(
|
|
1673
|
+
const contract = new Contract(
|
|
1453
1674
|
tokenPool,
|
|
1454
1675
|
interfaces.TokenPool_v2_0,
|
|
1455
1676
|
this.provider,
|
|
1456
1677
|
) as unknown as TypedContract<typeof TokenPool_2_0_ABI>
|
|
1678
|
+
token = contract.getToken()
|
|
1457
1679
|
router = contract.getDynamicConfig().then(([router]) => router)
|
|
1458
1680
|
minBlockConfirmations = contract.getMinBlockConfirmations().catch((err) => {
|
|
1459
1681
|
if (isError(err, 'CALL_EXCEPTION')) return 0
|
|
1460
1682
|
throw CCIPError.from(err)
|
|
1461
1683
|
})
|
|
1684
|
+
if (feeOpts) {
|
|
1685
|
+
tokenTransferFeeConfig = token.then((tokenAddr) =>
|
|
1686
|
+
contract
|
|
1687
|
+
.getTokenTransferFeeConfig(
|
|
1688
|
+
tokenAddr as string,
|
|
1689
|
+
feeOpts.destChainSelector,
|
|
1690
|
+
BigInt(feeOpts.blockConfirmationsRequested),
|
|
1691
|
+
feeOpts.tokenArgs,
|
|
1692
|
+
)
|
|
1693
|
+
.then((result) => ({
|
|
1694
|
+
destGasOverhead: Number(result.destGasOverhead),
|
|
1695
|
+
destBytesOverhead: Number(result.destBytesOverhead),
|
|
1696
|
+
defaultBlockConfirmationsFeeUSDCents: Number(
|
|
1697
|
+
result.defaultBlockConfirmationsFeeUSDCents,
|
|
1698
|
+
),
|
|
1699
|
+
customBlockConfirmationsFeeUSDCents: Number(
|
|
1700
|
+
result.customBlockConfirmationsFeeUSDCents,
|
|
1701
|
+
),
|
|
1702
|
+
defaultBlockConfirmationsTransferFeeBps: Number(
|
|
1703
|
+
result.defaultBlockConfirmationsTransferFeeBps,
|
|
1704
|
+
),
|
|
1705
|
+
customBlockConfirmationsTransferFeeBps: Number(
|
|
1706
|
+
result.customBlockConfirmationsTransferFeeBps,
|
|
1707
|
+
),
|
|
1708
|
+
isEnabled: result.isEnabled,
|
|
1709
|
+
}))
|
|
1710
|
+
.catch((err) => {
|
|
1711
|
+
if (isError(err, 'CALL_EXCEPTION')) return undefined
|
|
1712
|
+
throw CCIPError.from(err, 'UNKNOWN')
|
|
1713
|
+
}),
|
|
1714
|
+
)
|
|
1715
|
+
}
|
|
1462
1716
|
}
|
|
1463
|
-
const token = contract.getToken()
|
|
1464
1717
|
|
|
1465
|
-
return Promise.all([token, router, minBlockConfirmations]).then(
|
|
1466
|
-
([token, router, minBlockConfirmations]) => {
|
|
1718
|
+
return Promise.all([token, router, minBlockConfirmations, tokenTransferFeeConfig]).then(
|
|
1719
|
+
([token, router, minBlockConfirmations, tokenTransferFeeConfig]) => {
|
|
1467
1720
|
return {
|
|
1468
1721
|
token: token as CleanAddressable<typeof token>,
|
|
1469
1722
|
router: router as CleanAddressable<typeof router>,
|
|
@@ -1471,6 +1724,7 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
|
|
|
1471
1724
|
...(minBlockConfirmations != null && {
|
|
1472
1725
|
minBlockConfirmations: Number(minBlockConfirmations),
|
|
1473
1726
|
}),
|
|
1727
|
+
...(tokenTransferFeeConfig != null && { tokenTransferFeeConfig }),
|
|
1474
1728
|
}
|
|
1475
1729
|
},
|
|
1476
1730
|
)
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,10 @@ export type {
|
|
|
37
37
|
TokenInfo,
|
|
38
38
|
TokenPoolConfig,
|
|
39
39
|
TokenPoolRemote,
|
|
40
|
+
TokenTransferFee,
|
|
41
|
+
TokenTransferFeeConfig,
|
|
42
|
+
TokenTransferFeeOpts,
|
|
43
|
+
TotalFeesEstimate,
|
|
40
44
|
} from './chain.ts'
|
|
41
45
|
export { DEFAULT_API_RETRY_CONFIG, LaneFeature } from './chain.ts'
|
|
42
46
|
export { calculateManualExecProof, discoverOffRamp } from './execution.ts'
|
|
@@ -51,8 +55,8 @@ export {
|
|
|
51
55
|
encodeExtraArgs,
|
|
52
56
|
} from './extra-args.ts'
|
|
53
57
|
export { estimateReceiveExecution } from './gas.ts'
|
|
54
|
-
export { getOffchainTokenData } from './offchain.ts'
|
|
55
|
-
export { decodeMessage,
|
|
58
|
+
export { CCTP_FINALITY_FAST, CCTP_FINALITY_STANDARD, getOffchainTokenData } from './offchain.ts'
|
|
59
|
+
export { decodeMessage, sourceToDestTokenAddresses } from './requests.ts'
|
|
56
60
|
export {
|
|
57
61
|
type CCIPExecution,
|
|
58
62
|
type CCIPMessage,
|
package/src/offchain.ts
CHANGED
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
CCIPLbtcAttestationNotApprovedError,
|
|
6
6
|
CCIPLbtcAttestationNotFoundError,
|
|
7
7
|
CCIPUsdcAttestationError,
|
|
8
|
+
CCIPUsdcBurnFeesError,
|
|
8
9
|
} from './errors/index.ts'
|
|
9
10
|
import { parseSourceTokenData } from './evm/messages.ts'
|
|
10
11
|
import { type CCIPRequest, type OffchainTokenData, type WithLogger, NetworkType } from './types.ts'
|
|
11
|
-
import { getDataBytes, networkInfo } from './utils.ts'
|
|
12
|
+
import { fetchWithTimeout, getDataBytes, networkInfo } from './utils.ts'
|
|
12
13
|
|
|
13
14
|
const CIRCLE_API_URL = {
|
|
14
15
|
mainnet: 'https://iris-api.circle.com',
|
|
@@ -60,6 +61,57 @@ export async function getUsdcAttestation(
|
|
|
60
61
|
return att
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* CCTP V2 finality tier identifiers returned by Circle's burn-fees API.
|
|
66
|
+
*
|
|
67
|
+
* These are **opaque tier IDs**, not block counts or durations.
|
|
68
|
+
* The CCTP V2 whitepaper (Section 8, Table 2) defines exactly two tiers today;
|
|
69
|
+
* additional tiers may be added in the future (the wide spacing between values
|
|
70
|
+
* is intentional to leave room).
|
|
71
|
+
*
|
|
72
|
+
* @see https://developers.circle.com/cctp/concepts/fees
|
|
73
|
+
* @see CCTP V2 Whitepaper, Section 8 — "Finality Levels"
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/** Fast / pre-finality tier: attested seconds after soft confirmation. */
|
|
77
|
+
export const CCTP_FINALITY_FAST = 1000
|
|
78
|
+
|
|
79
|
+
/** Standard / finalized tier: attested after full on-chain finality. */
|
|
80
|
+
export const CCTP_FINALITY_STANDARD = 2000
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fetches USDC burn fee tiers from Circle's CCTP API.
|
|
84
|
+
*
|
|
85
|
+
* @param sourceDomain - CCTP source domain identifier
|
|
86
|
+
* @param destDomain - CCTP destination domain identifier
|
|
87
|
+
* @param networkType - network type (mainnet or testnet)
|
|
88
|
+
* @returns Array of fee tiers with finality thresholds and BPS fees
|
|
89
|
+
*/
|
|
90
|
+
export async function getUsdcBurnFees(
|
|
91
|
+
sourceDomain: number,
|
|
92
|
+
destDomain: number,
|
|
93
|
+
networkType: NetworkType,
|
|
94
|
+
): Promise<{ finalityThreshold: number; minimumFee: number }[]> {
|
|
95
|
+
const baseUrl =
|
|
96
|
+
networkType === NetworkType.Mainnet ? CIRCLE_API_URL.mainnet : CIRCLE_API_URL.testnet
|
|
97
|
+
const url = `${baseUrl}/v2/burn/USDC/fees/${sourceDomain}/${destDomain}`
|
|
98
|
+
const res = await fetchWithTimeout(url, 'getUsdcBurnFees')
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
throw new CCIPUsdcBurnFeesError(sourceDomain, destDomain, res.status)
|
|
101
|
+
}
|
|
102
|
+
const json: unknown = await res.json()
|
|
103
|
+
if (!Array.isArray(json)) {
|
|
104
|
+
throw new CCIPUsdcBurnFeesError(sourceDomain, destDomain, res.status)
|
|
105
|
+
}
|
|
106
|
+
for (const tier of json) {
|
|
107
|
+
const t = tier as Record<string, unknown>
|
|
108
|
+
if (typeof t.finalityThreshold !== 'number' || typeof t.minimumFee !== 'number') {
|
|
109
|
+
throw new CCIPUsdcBurnFeesError(sourceDomain, destDomain, res.status)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return json as { finalityThreshold: number; minimumFee: number }[]
|
|
113
|
+
}
|
|
114
|
+
|
|
63
115
|
const LOMBARD_API_URL = {
|
|
64
116
|
mainnet: 'https://mainnet.prod.lombard.finance',
|
|
65
117
|
testnet: 'https://gastald-testnet.prod.lombard.finance',
|
package/src/requests.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type BytesLike, hexlify, isBytesLike, toBigInt } from 'ethers'
|
|
2
2
|
import type { PickDeep } from 'type-fest'
|
|
3
3
|
|
|
4
|
-
import { type ChainStatic,
|
|
4
|
+
import { type ChainStatic, Chain } from './chain.ts'
|
|
5
5
|
import {
|
|
6
6
|
CCIPChainFamilyUnsupportedError,
|
|
7
7
|
CCIPMessageBatchIncompleteError,
|
|
@@ -393,64 +393,6 @@ export async function getMessagesInBatch<
|
|
|
393
393
|
return messages
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
/**
|
|
397
|
-
* Fetches CCIP requests originated by a specific sender.
|
|
398
|
-
* @param source - Source chain instance.
|
|
399
|
-
* @param sender - Sender address.
|
|
400
|
-
* @param filter - Log filter options.
|
|
401
|
-
* @returns Async generator of CCIP requests.
|
|
402
|
-
* @throws {@link CCIPChainFamilyUnsupportedError} if chain family not supported for legacy messages
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* ```typescript
|
|
406
|
-
* import { getMessagesForSender, EVMChain } from '@chainlink/ccip-sdk'
|
|
407
|
-
*
|
|
408
|
-
* const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
|
|
409
|
-
*
|
|
410
|
-
* for await (const request of getMessagesForSender(chain, '0xSenderAddress', {})) {
|
|
411
|
-
* console.log('Message ID:', request.message.messageId)
|
|
412
|
-
* console.log('Destination:', request.lane.destChainSelector)
|
|
413
|
-
* }
|
|
414
|
-
* ```
|
|
415
|
-
*
|
|
416
|
-
* @see {@link getMessagesInTx} - Fetch from specific transaction
|
|
417
|
-
* @see {@link getMessageById} - Search by messageId
|
|
418
|
-
*/
|
|
419
|
-
export async function* getMessagesForSender(
|
|
420
|
-
source: Chain,
|
|
421
|
-
sender: string,
|
|
422
|
-
filter: Pick<LogFilter, 'address' | 'startBlock' | 'startTime' | 'endBlock'>,
|
|
423
|
-
): AsyncGenerator<Omit<CCIPRequest, 'tx' | 'timestamp'>, void, unknown> {
|
|
424
|
-
const filterWithSender = {
|
|
425
|
-
...filter,
|
|
426
|
-
sender, // some chain families may use this to look for account lookup/narrow down the search
|
|
427
|
-
topics: ['CCIPSendRequested', 'CCIPMessageSent'],
|
|
428
|
-
}
|
|
429
|
-
for await (const log of source.getLogs(filterWithSender)) {
|
|
430
|
-
const message = (source.constructor as ChainStatic).decodeMessage(log)
|
|
431
|
-
if (message?.sender !== sender) continue
|
|
432
|
-
let destChainSelector, version
|
|
433
|
-
if ('destChainSelector' in message) {
|
|
434
|
-
destChainSelector = message.destChainSelector
|
|
435
|
-
;[, version] = await source.typeAndVersion(log.address)
|
|
436
|
-
} else if (source.network.family === ChainFamily.EVM) {
|
|
437
|
-
;({ destChainSelector, version } = await (source as EVMChain).getLaneForOnRamp(log.address))
|
|
438
|
-
} else {
|
|
439
|
-
throw new CCIPChainFamilyUnsupportedError(source.network.family)
|
|
440
|
-
}
|
|
441
|
-
yield {
|
|
442
|
-
lane: {
|
|
443
|
-
sourceChainSelector: source.network.chainSelector,
|
|
444
|
-
destChainSelector,
|
|
445
|
-
onRamp: log.address,
|
|
446
|
-
version: version as CCIPVersion,
|
|
447
|
-
},
|
|
448
|
-
message,
|
|
449
|
-
log,
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
396
|
/**
|
|
455
397
|
* Map source token to its pool address and destination token address.
|
|
456
398
|
*
|
package/src/solana/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type LogFilter,
|
|
36
36
|
type TokenInfo,
|
|
37
37
|
type TokenPoolRemote,
|
|
38
|
+
type TokenTransferFeeOpts,
|
|
38
39
|
Chain,
|
|
39
40
|
} from '../chain.ts'
|
|
40
41
|
import {
|
|
@@ -1366,7 +1367,10 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
|
|
|
1366
1367
|
* {@inheritDoc Chain.getTokenPoolConfig}
|
|
1367
1368
|
* @throws {@link CCIPTokenPoolStateNotFoundError} if token pool state not found
|
|
1368
1369
|
*/
|
|
1369
|
-
async getTokenPoolConfig(
|
|
1370
|
+
async getTokenPoolConfig(
|
|
1371
|
+
tokenPool: string,
|
|
1372
|
+
_feeOpts?: TokenTransferFeeOpts,
|
|
1373
|
+
): Promise<{
|
|
1370
1374
|
token: string
|
|
1371
1375
|
router: string
|
|
1372
1376
|
tokenPoolProgram: string
|
package/src/sui/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type ChainStatic,
|
|
13
13
|
type GetBalanceOpts,
|
|
14
14
|
type LogFilter,
|
|
15
|
+
type TokenTransferFeeOpts,
|
|
15
16
|
Chain,
|
|
16
17
|
} from '../chain.ts'
|
|
17
18
|
import { getCcipStateAddress, getOffRampForCcip } from './discovery.ts'
|
|
@@ -793,7 +794,7 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
|
|
|
793
794
|
}
|
|
794
795
|
|
|
795
796
|
/** {@inheritDoc Chain.getTokenPoolConfig} */
|
|
796
|
-
async getTokenPoolConfig(_tokenPool: string): Promise<never> {
|
|
797
|
+
async getTokenPoolConfig(_tokenPool: string, _feeOpts?: TokenTransferFeeOpts): Promise<never> {
|
|
797
798
|
return Promise.reject(new CCIPNotImplementedError('SuiChain.getTokenPoolConfig'))
|
|
798
799
|
}
|
|
799
800
|
|