@usewhisk/cctp 0.0.0 → 0.1.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/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/abi.cjs +239 -0
- package/dist/abi.cjs.map +1 -0
- package/dist/abi.d.cts +401 -0
- package/dist/abi.d.ts +401 -0
- package/dist/abi.js +234 -0
- package/dist/abi.js.map +1 -0
- package/dist/attestation-D52iUV0y.d.cts +188 -0
- package/dist/attestation-D52iUV0y.d.ts +188 -0
- package/dist/chains.cjs +1621 -0
- package/dist/chains.cjs.map +1 -0
- package/dist/chains.d.cts +2 -0
- package/dist/chains.d.ts +2 -0
- package/dist/chains.js +1514 -0
- package/dist/chains.js.map +1 -0
- package/dist/index-BIerSvES.d.cts +4070 -0
- package/dist/index-O0Ms-Fa5.d.ts +4070 -0
- package/dist/index.cjs +4003 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +940 -0
- package/dist/index.d.ts +940 -0
- package/dist/index.js +3785 -0
- package/dist/index.js.map +1 -0
- package/dist/iris.cjs +557 -0
- package/dist/iris.cjs.map +1 -0
- package/dist/iris.d.cts +205 -0
- package/dist/iris.d.ts +205 -0
- package/dist/iris.js +538 -0
- package/dist/iris.js.map +1 -0
- package/package.json +85 -10
package/dist/index.js
ADDED
|
@@ -0,0 +1,3785 @@
|
|
|
1
|
+
import { Asset, TransactionBuilder, Account, BASE_FEE, Contract, nativeToScVal, StrKey, Address, scValToNative, xdr } from '@stellar/stellar-sdk';
|
|
2
|
+
import { hash, cairo, num } from 'starknet';
|
|
3
|
+
import { encodeFunctionData } from 'viem';
|
|
4
|
+
import { PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js';
|
|
5
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
6
|
+
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
7
|
+
import { Buffer as Buffer$1 } from 'buffer';
|
|
8
|
+
|
|
9
|
+
// src/errors.ts
|
|
10
|
+
var CctpError = class extends Error {
|
|
11
|
+
code;
|
|
12
|
+
cause;
|
|
13
|
+
constructor(code, message, cause) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "CctpError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
if (cause !== void 0) this.cause = cause;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var InvalidInputError = class extends CctpError {
|
|
21
|
+
constructor(code, message, cause) {
|
|
22
|
+
super(code, message, cause);
|
|
23
|
+
this.name = "InvalidInputError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var IrisError = class extends CctpError {
|
|
27
|
+
constructor(code, message, cause) {
|
|
28
|
+
super(code, message, cause);
|
|
29
|
+
this.name = "IrisError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var IrisHttpError = class extends IrisError {
|
|
33
|
+
status;
|
|
34
|
+
bodySnippet;
|
|
35
|
+
url;
|
|
36
|
+
constructor(status, url, bodySnippet) {
|
|
37
|
+
super(
|
|
38
|
+
"iris_http",
|
|
39
|
+
`Iris ${status} on ${url}: ${bodySnippet.slice(0, 200)}`
|
|
40
|
+
);
|
|
41
|
+
this.name = "IrisHttpError";
|
|
42
|
+
this.status = status;
|
|
43
|
+
this.url = url;
|
|
44
|
+
this.bodySnippet = bodySnippet;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var IrisRateLimitedError = class extends IrisError {
|
|
48
|
+
retryAt;
|
|
49
|
+
host;
|
|
50
|
+
constructor(host, retryAt) {
|
|
51
|
+
super(
|
|
52
|
+
"iris_rate_limited",
|
|
53
|
+
`Iris rate limit reached on ${host}; requests resume at ${new Date(retryAt).toISOString()}`
|
|
54
|
+
);
|
|
55
|
+
this.name = "IrisRateLimitedError";
|
|
56
|
+
this.host = host;
|
|
57
|
+
this.retryAt = retryAt;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var IrisNetworkError = class extends IrisError {
|
|
61
|
+
constructor(message, cause) {
|
|
62
|
+
super("iris_network", message, cause);
|
|
63
|
+
this.name = "IrisNetworkError";
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var IrisParseError = class extends IrisError {
|
|
67
|
+
constructor(message, cause) {
|
|
68
|
+
super("iris_parse", message, cause);
|
|
69
|
+
this.name = "IrisParseError";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var IrisAbortedError = class extends IrisError {
|
|
73
|
+
constructor(message) {
|
|
74
|
+
super("iris_aborted", message);
|
|
75
|
+
this.name = "IrisAbortedError";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var ProtocolError = class extends CctpError {
|
|
79
|
+
constructor(code, message, cause) {
|
|
80
|
+
super(code, message, cause);
|
|
81
|
+
this.name = "ProtocolError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var UnsupportedError = class extends CctpError {
|
|
85
|
+
constructor(code, message) {
|
|
86
|
+
super(code, message);
|
|
87
|
+
this.name = "UnsupportedError";
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
function isCctpError(value) {
|
|
91
|
+
return value instanceof CctpError;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/chains/constants.ts
|
|
95
|
+
var TOKEN_MESSENGER_V2_EVM_MAINNET = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d";
|
|
96
|
+
var MESSAGE_TRANSMITTER_V2_EVM_MAINNET = "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64";
|
|
97
|
+
var TOKEN_MESSENGER_V2_EVM_TESTNET = "0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA";
|
|
98
|
+
var MESSAGE_TRANSMITTER_V2_EVM_TESTNET = "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275";
|
|
99
|
+
var TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET = "0x71f54F818671cD0D7ea140Da213e5C8b5C92a408";
|
|
100
|
+
var TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET = "0x8745D906D67C346E5eb1aEEED38Eb87F34DF0C0A";
|
|
101
|
+
var TOKEN_MESSENGER_MINTER_V2_SOLANA = "CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe";
|
|
102
|
+
var MESSAGE_TRANSMITTER_V2_SOLANA = "CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC";
|
|
103
|
+
var USDC_SPL_MAINNET = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
104
|
+
var USDC_SPL_DEVNET = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU";
|
|
105
|
+
var SOLANA_CCTP_DOMAIN = 5;
|
|
106
|
+
var TOKEN_MESSENGER_MINTER_STELLAR_MAINNET = "CAE2G5Z77UP7GYPYGFOWFGW7C7J6I4YP2AFGSADRKQY62SYUFLPNFTXL";
|
|
107
|
+
var MESSAGE_TRANSMITTER_STELLAR_MAINNET = "CACMENFFJPJMSDAJQLX4R7K3SFZIW2LJSE3R2UMLGSWHFHS353FVXAZV";
|
|
108
|
+
var CCTP_FORWARDER_STELLAR_MAINNET = "CBZL2IH7F6BIDAA3WBNXYKIXSATJGMSW7K5P5MJ6STX5RXN47TZJDF5T";
|
|
109
|
+
var TOKEN_MESSENGER_MINTER_STELLAR_TESTNET = "CDNG7HXAPBWICI2E3AUBP3YZWZELJLYSB6F5CC7WLDTLTHVM74SLRTHP";
|
|
110
|
+
var MESSAGE_TRANSMITTER_STELLAR_TESTNET = "CBJ6MTCKKZG73PMDZCJMSFRD7DQEMI4FKDH7CGDSV4W6FHCRBCQAVVJY";
|
|
111
|
+
var CCTP_FORWARDER_STELLAR_TESTNET = "CA66Q2WFBND6V4UEB7RD4SAXSVIWMD6RA4X3U32ELVFGXV5PJK4T4VSZ";
|
|
112
|
+
var USDC_ISSUER_STELLAR_MAINNET = "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN";
|
|
113
|
+
var USDC_ISSUER_STELLAR_TESTNET = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5";
|
|
114
|
+
var STELLAR_NETWORK_PASSPHRASE_MAINNET = "Public Global Stellar Network ; September 2015";
|
|
115
|
+
var STELLAR_NETWORK_PASSPHRASE_TESTNET = "Test SDF Network ; September 2015";
|
|
116
|
+
var STELLAR_CCTP_DOMAIN = 27;
|
|
117
|
+
var STELLAR_USDC_DECIMALS = 7;
|
|
118
|
+
var CCTP_USDC_DECIMALS = 6;
|
|
119
|
+
var TOKEN_MESSENGER_MINTER_V2_STARKNET_MAINNET = "0x07d421B9cA8aA32DF259965cDA8ACb93F7599F69209A41872AE84638B2A20F2a";
|
|
120
|
+
var MESSAGE_TRANSMITTER_V2_STARKNET_MAINNET = "0x02EBB5777B6dD8B26ea11D68Fdf1D2c85cD2099335328Be845a28c77A8AEf183";
|
|
121
|
+
var TOKEN_MESSENGER_MINTER_V2_STARKNET_SEPOLIA = "0x04bDdE1E09a4B09a2F95d893D94a967b7717eB85A3f6dEcA8c080Ee01fBc3370";
|
|
122
|
+
var MESSAGE_TRANSMITTER_V2_STARKNET_SEPOLIA = "0x04db7926C64f1f32a840F3Fa95cB551f3801a3600Bae87aF87807A54DCE12Fe8";
|
|
123
|
+
var USDC_STARKNET_MAINNET = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
|
|
124
|
+
var USDC_STARKNET_SEPOLIA = "0x0512feAc6339Ff7889822cb5aA2a86C848e9D392bB0E3E237C008674feeD8343";
|
|
125
|
+
var STARKNET_CHAIN_ID_MAINNET = "0x534e5f4d41494e";
|
|
126
|
+
var STARKNET_CHAIN_ID_SEPOLIA = "0x534e5f5345504f4c4941";
|
|
127
|
+
var STARKNET_CCTP_DOMAIN = 25;
|
|
128
|
+
|
|
129
|
+
// src/chains/mainnet.ts
|
|
130
|
+
var Ethereum = {
|
|
131
|
+
id: "Ethereum",
|
|
132
|
+
label: "Ethereum",
|
|
133
|
+
ecosystem: "evm",
|
|
134
|
+
network: "mainnet",
|
|
135
|
+
evmChainId: 1,
|
|
136
|
+
cctpDomain: 0,
|
|
137
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
138
|
+
usdcDecimals: 6,
|
|
139
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
140
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
141
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
142
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
143
|
+
defaultRpcUrls: [
|
|
144
|
+
"https://ethereum-rpc.publicnode.com",
|
|
145
|
+
"https://ethereum.publicnode.com"
|
|
146
|
+
],
|
|
147
|
+
explorerUrl: "https://etherscan.io",
|
|
148
|
+
capabilities: {
|
|
149
|
+
fastSource: true,
|
|
150
|
+
forwardingDestination: true,
|
|
151
|
+
upfrontFeeSource: true,
|
|
152
|
+
standardFeeSwitch: false
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var Avalanche = {
|
|
156
|
+
id: "Avalanche",
|
|
157
|
+
label: "Avalanche",
|
|
158
|
+
ecosystem: "evm",
|
|
159
|
+
network: "mainnet",
|
|
160
|
+
evmChainId: 43114,
|
|
161
|
+
cctpDomain: 1,
|
|
162
|
+
usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
163
|
+
usdcDecimals: 6,
|
|
164
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
165
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
166
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
167
|
+
nativeCurrency: { symbol: "AVAX", decimals: 18 },
|
|
168
|
+
defaultRpcUrls: ["https://api.avax.network/ext/bc/C/rpc"],
|
|
169
|
+
explorerUrl: "https://explorer.avax.network/c-chain",
|
|
170
|
+
capabilities: {
|
|
171
|
+
fastSource: false,
|
|
172
|
+
forwardingDestination: true,
|
|
173
|
+
upfrontFeeSource: true,
|
|
174
|
+
standardFeeSwitch: false
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var Optimism = {
|
|
178
|
+
id: "Optimism",
|
|
179
|
+
label: "OP Mainnet",
|
|
180
|
+
ecosystem: "evm",
|
|
181
|
+
network: "mainnet",
|
|
182
|
+
evmChainId: 10,
|
|
183
|
+
cctpDomain: 2,
|
|
184
|
+
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
185
|
+
usdcDecimals: 6,
|
|
186
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
187
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
188
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
189
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
190
|
+
defaultRpcUrls: ["https://mainnet.optimism.io"],
|
|
191
|
+
explorerUrl: "https://explorer.optimism.io",
|
|
192
|
+
capabilities: {
|
|
193
|
+
fastSource: true,
|
|
194
|
+
forwardingDestination: true,
|
|
195
|
+
upfrontFeeSource: true,
|
|
196
|
+
standardFeeSwitch: false
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
var Arbitrum = {
|
|
200
|
+
id: "Arbitrum",
|
|
201
|
+
label: "Arbitrum",
|
|
202
|
+
ecosystem: "evm",
|
|
203
|
+
network: "mainnet",
|
|
204
|
+
evmChainId: 42161,
|
|
205
|
+
cctpDomain: 3,
|
|
206
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
207
|
+
usdcDecimals: 6,
|
|
208
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
209
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
210
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
211
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
212
|
+
defaultRpcUrls: ["https://arb1.arbitrum.io/rpc"],
|
|
213
|
+
explorerUrl: "https://arbiscan.io",
|
|
214
|
+
capabilities: {
|
|
215
|
+
fastSource: true,
|
|
216
|
+
forwardingDestination: true,
|
|
217
|
+
upfrontFeeSource: true,
|
|
218
|
+
standardFeeSwitch: false
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var Base = {
|
|
222
|
+
id: "Base",
|
|
223
|
+
label: "Base",
|
|
224
|
+
ecosystem: "evm",
|
|
225
|
+
network: "mainnet",
|
|
226
|
+
evmChainId: 8453,
|
|
227
|
+
cctpDomain: 6,
|
|
228
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
229
|
+
usdcDecimals: 6,
|
|
230
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
231
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
232
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
233
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
234
|
+
defaultRpcUrls: ["https://mainnet.base.org"],
|
|
235
|
+
explorerUrl: "https://basescan.org",
|
|
236
|
+
capabilities: {
|
|
237
|
+
fastSource: true,
|
|
238
|
+
forwardingDestination: true,
|
|
239
|
+
upfrontFeeSource: true,
|
|
240
|
+
standardFeeSwitch: false
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var Polygon = {
|
|
244
|
+
id: "Polygon",
|
|
245
|
+
label: "Polygon PoS",
|
|
246
|
+
ecosystem: "evm",
|
|
247
|
+
network: "mainnet",
|
|
248
|
+
evmChainId: 137,
|
|
249
|
+
cctpDomain: 7,
|
|
250
|
+
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
251
|
+
usdcDecimals: 6,
|
|
252
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
253
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
254
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
255
|
+
nativeCurrency: { symbol: "POL", decimals: 18 },
|
|
256
|
+
defaultRpcUrls: [
|
|
257
|
+
"https://polygon.drpc.org",
|
|
258
|
+
"https://polygon.publicnode.com"
|
|
259
|
+
],
|
|
260
|
+
explorerUrl: "https://polygonscan.com",
|
|
261
|
+
capabilities: {
|
|
262
|
+
fastSource: false,
|
|
263
|
+
forwardingDestination: true,
|
|
264
|
+
upfrontFeeSource: true,
|
|
265
|
+
standardFeeSwitch: false
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var Unichain = {
|
|
269
|
+
id: "Unichain",
|
|
270
|
+
label: "Unichain",
|
|
271
|
+
ecosystem: "evm",
|
|
272
|
+
network: "mainnet",
|
|
273
|
+
evmChainId: 130,
|
|
274
|
+
cctpDomain: 10,
|
|
275
|
+
usdc: "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
|
|
276
|
+
usdcDecimals: 6,
|
|
277
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
278
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
279
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
280
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
281
|
+
defaultRpcUrls: ["https://mainnet.unichain.org"],
|
|
282
|
+
explorerUrl: "https://uniscan.xyz",
|
|
283
|
+
capabilities: {
|
|
284
|
+
fastSource: true,
|
|
285
|
+
forwardingDestination: true,
|
|
286
|
+
upfrontFeeSource: true,
|
|
287
|
+
standardFeeSwitch: false
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
var Linea = {
|
|
291
|
+
id: "Linea",
|
|
292
|
+
label: "Linea",
|
|
293
|
+
ecosystem: "evm",
|
|
294
|
+
network: "mainnet",
|
|
295
|
+
evmChainId: 59144,
|
|
296
|
+
cctpDomain: 11,
|
|
297
|
+
usdc: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff",
|
|
298
|
+
usdcDecimals: 6,
|
|
299
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
300
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
301
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
302
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
303
|
+
defaultRpcUrls: ["https://rpc.linea.build"],
|
|
304
|
+
explorerUrl: "https://lineascan.build",
|
|
305
|
+
capabilities: {
|
|
306
|
+
fastSource: true,
|
|
307
|
+
forwardingDestination: true,
|
|
308
|
+
upfrontFeeSource: true,
|
|
309
|
+
standardFeeSwitch: false
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var Codex = {
|
|
313
|
+
id: "Codex",
|
|
314
|
+
label: "Codex",
|
|
315
|
+
ecosystem: "evm",
|
|
316
|
+
network: "mainnet",
|
|
317
|
+
evmChainId: 81224,
|
|
318
|
+
cctpDomain: 12,
|
|
319
|
+
usdc: "0xd996633a415985DBd7D6D12f4A4343E31f5037cf",
|
|
320
|
+
usdcDecimals: 6,
|
|
321
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
322
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
323
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
324
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
325
|
+
defaultRpcUrls: ["https://rpc.codex.xyz"],
|
|
326
|
+
explorerUrl: "https://explorer.codex.xyz",
|
|
327
|
+
capabilities: {
|
|
328
|
+
fastSource: true,
|
|
329
|
+
forwardingDestination: true,
|
|
330
|
+
upfrontFeeSource: true,
|
|
331
|
+
standardFeeSwitch: false
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var Sonic = {
|
|
335
|
+
id: "Sonic",
|
|
336
|
+
label: "Sonic",
|
|
337
|
+
ecosystem: "evm",
|
|
338
|
+
network: "mainnet",
|
|
339
|
+
evmChainId: 146,
|
|
340
|
+
cctpDomain: 13,
|
|
341
|
+
usdc: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
|
|
342
|
+
usdcDecimals: 6,
|
|
343
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
344
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
345
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
346
|
+
nativeCurrency: { symbol: "S", decimals: 18 },
|
|
347
|
+
defaultRpcUrls: ["https://rpc.soniclabs.com"],
|
|
348
|
+
explorerUrl: "https://sonicscan.org",
|
|
349
|
+
capabilities: {
|
|
350
|
+
fastSource: false,
|
|
351
|
+
forwardingDestination: true,
|
|
352
|
+
upfrontFeeSource: true,
|
|
353
|
+
standardFeeSwitch: false
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
var WorldChain = {
|
|
357
|
+
id: "World_Chain",
|
|
358
|
+
label: "World Chain",
|
|
359
|
+
ecosystem: "evm",
|
|
360
|
+
network: "mainnet",
|
|
361
|
+
evmChainId: 480,
|
|
362
|
+
cctpDomain: 14,
|
|
363
|
+
usdc: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1",
|
|
364
|
+
usdcDecimals: 6,
|
|
365
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
366
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
367
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
368
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
369
|
+
defaultRpcUrls: ["https://worldchain-mainnet.g.alchemy.com/public"],
|
|
370
|
+
explorerUrl: "https://worldscan.org",
|
|
371
|
+
capabilities: {
|
|
372
|
+
fastSource: true,
|
|
373
|
+
forwardingDestination: true,
|
|
374
|
+
upfrontFeeSource: true,
|
|
375
|
+
standardFeeSwitch: false
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
var Monad = {
|
|
379
|
+
id: "Monad",
|
|
380
|
+
label: "Monad",
|
|
381
|
+
ecosystem: "evm",
|
|
382
|
+
network: "mainnet",
|
|
383
|
+
evmChainId: 143,
|
|
384
|
+
cctpDomain: 15,
|
|
385
|
+
usdc: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
|
|
386
|
+
usdcDecimals: 6,
|
|
387
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
388
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
389
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
390
|
+
nativeCurrency: { symbol: "MON", decimals: 18 },
|
|
391
|
+
defaultRpcUrls: ["https://rpc.monad.xyz", "https://rpc1.monad.xyz"],
|
|
392
|
+
explorerUrl: "https://monadvision.com",
|
|
393
|
+
capabilities: {
|
|
394
|
+
fastSource: false,
|
|
395
|
+
forwardingDestination: true,
|
|
396
|
+
upfrontFeeSource: true,
|
|
397
|
+
standardFeeSwitch: true
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
var Sei = {
|
|
401
|
+
id: "Sei",
|
|
402
|
+
label: "Sei",
|
|
403
|
+
ecosystem: "evm",
|
|
404
|
+
network: "mainnet",
|
|
405
|
+
evmChainId: 1329,
|
|
406
|
+
cctpDomain: 16,
|
|
407
|
+
usdc: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
|
|
408
|
+
usdcDecimals: 6,
|
|
409
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
410
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
411
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
412
|
+
nativeCurrency: { symbol: "SEI", decimals: 18 },
|
|
413
|
+
defaultRpcUrls: ["https://evm-rpc.sei-apis.com"],
|
|
414
|
+
explorerUrl: "https://seiscan.io",
|
|
415
|
+
capabilities: {
|
|
416
|
+
fastSource: false,
|
|
417
|
+
forwardingDestination: true,
|
|
418
|
+
upfrontFeeSource: true,
|
|
419
|
+
standardFeeSwitch: true
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
var XDC = {
|
|
423
|
+
id: "XDC",
|
|
424
|
+
label: "XDC",
|
|
425
|
+
ecosystem: "evm",
|
|
426
|
+
network: "mainnet",
|
|
427
|
+
evmChainId: 50,
|
|
428
|
+
cctpDomain: 18,
|
|
429
|
+
usdc: "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
|
|
430
|
+
usdcDecimals: 6,
|
|
431
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
432
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
433
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
434
|
+
nativeCurrency: { symbol: "XDC", decimals: 18 },
|
|
435
|
+
defaultRpcUrls: ["https://erpc.xinfin.network", "https://rpc.xinfin.network"],
|
|
436
|
+
explorerUrl: "https://xdcscan.io",
|
|
437
|
+
capabilities: {
|
|
438
|
+
fastSource: false,
|
|
439
|
+
forwardingDestination: true,
|
|
440
|
+
upfrontFeeSource: true,
|
|
441
|
+
standardFeeSwitch: true
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var HyperEVM = {
|
|
445
|
+
id: "HyperEVM",
|
|
446
|
+
label: "HyperEVM",
|
|
447
|
+
ecosystem: "evm",
|
|
448
|
+
network: "mainnet",
|
|
449
|
+
evmChainId: 999,
|
|
450
|
+
cctpDomain: 19,
|
|
451
|
+
usdc: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
452
|
+
usdcDecimals: 6,
|
|
453
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
454
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
455
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
456
|
+
nativeCurrency: { symbol: "HYPE", decimals: 18 },
|
|
457
|
+
defaultRpcUrls: ["https://rpc.hyperliquid.xyz/evm"],
|
|
458
|
+
explorerUrl: "https://hyperevmscan.io",
|
|
459
|
+
capabilities: {
|
|
460
|
+
fastSource: false,
|
|
461
|
+
forwardingDestination: true,
|
|
462
|
+
upfrontFeeSource: true,
|
|
463
|
+
standardFeeSwitch: true
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var Ink = {
|
|
467
|
+
id: "Ink",
|
|
468
|
+
label: "Ink",
|
|
469
|
+
ecosystem: "evm",
|
|
470
|
+
network: "mainnet",
|
|
471
|
+
evmChainId: 57073,
|
|
472
|
+
cctpDomain: 21,
|
|
473
|
+
usdc: "0x2D270e6886d130D724215A266106e6832161EAEd",
|
|
474
|
+
usdcDecimals: 6,
|
|
475
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
476
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
477
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
478
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
479
|
+
defaultRpcUrls: [
|
|
480
|
+
"https://rpc-gel.inkonchain.com",
|
|
481
|
+
"https://rpc-qnd.inkonchain.com"
|
|
482
|
+
],
|
|
483
|
+
explorerUrl: "https://explorer.inkonchain.com",
|
|
484
|
+
capabilities: {
|
|
485
|
+
fastSource: true,
|
|
486
|
+
forwardingDestination: true,
|
|
487
|
+
upfrontFeeSource: true,
|
|
488
|
+
standardFeeSwitch: true
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
var Plume = {
|
|
492
|
+
id: "Plume",
|
|
493
|
+
label: "Plume",
|
|
494
|
+
ecosystem: "evm",
|
|
495
|
+
network: "mainnet",
|
|
496
|
+
evmChainId: 98866,
|
|
497
|
+
cctpDomain: 22,
|
|
498
|
+
usdc: "0x222365EF19F7947e5484218551B56bb3965Aa7aF",
|
|
499
|
+
usdcDecimals: 6,
|
|
500
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
501
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
502
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
503
|
+
nativeCurrency: { symbol: "PLUME", decimals: 18 },
|
|
504
|
+
defaultRpcUrls: ["https://rpc.plume.org"],
|
|
505
|
+
explorerUrl: "https://explorer.plume.org",
|
|
506
|
+
capabilities: {
|
|
507
|
+
fastSource: true,
|
|
508
|
+
forwardingDestination: true,
|
|
509
|
+
upfrontFeeSource: true,
|
|
510
|
+
standardFeeSwitch: true
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var Arc = {
|
|
514
|
+
id: "Arc",
|
|
515
|
+
label: "Arc",
|
|
516
|
+
ecosystem: "evm",
|
|
517
|
+
network: "mainnet",
|
|
518
|
+
evmChainId: 5042,
|
|
519
|
+
cctpDomain: 26,
|
|
520
|
+
usdc: "0x3600000000000000000000000000000000000000",
|
|
521
|
+
usdcDecimals: 6,
|
|
522
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
523
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
524
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET,
|
|
525
|
+
nativeCurrency: { symbol: "USDC", decimals: 18 },
|
|
526
|
+
defaultRpcUrls: ["https://rpc.mainnet.arc.io"],
|
|
527
|
+
explorerUrl: "https://explorer.arc.io",
|
|
528
|
+
capabilities: {
|
|
529
|
+
fastSource: false,
|
|
530
|
+
forwardingDestination: true,
|
|
531
|
+
upfrontFeeSource: true,
|
|
532
|
+
standardFeeSwitch: true
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
var Edge = {
|
|
536
|
+
id: "Edge",
|
|
537
|
+
label: "EDGE",
|
|
538
|
+
ecosystem: "evm",
|
|
539
|
+
network: "mainnet",
|
|
540
|
+
evmChainId: 3343,
|
|
541
|
+
cctpDomain: 28,
|
|
542
|
+
usdc: "0x98d2919b9A214E6Fa5384AC81E6864bA686Ad74c",
|
|
543
|
+
usdcDecimals: 6,
|
|
544
|
+
tokenMessenger: "0x98706A006bc632Df31CAdFCBD43F38887ce2ca5c",
|
|
545
|
+
messageTransmitter: "0x5b61381Fc9e58E70EfC13a4A97516997019198ee",
|
|
546
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
547
|
+
defaultRpcUrls: ["https://edge-mainnet.g.alchemy.com/public"],
|
|
548
|
+
explorerUrl: "https://pro.edgex.exchange/en-US/explorer",
|
|
549
|
+
capabilities: {
|
|
550
|
+
fastSource: true,
|
|
551
|
+
forwardingDestination: true,
|
|
552
|
+
upfrontFeeSource: false,
|
|
553
|
+
standardFeeSwitch: true
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
var Injective = {
|
|
557
|
+
id: "Injective",
|
|
558
|
+
label: "Injective",
|
|
559
|
+
ecosystem: "evm",
|
|
560
|
+
network: "mainnet",
|
|
561
|
+
evmChainId: 1776,
|
|
562
|
+
cctpDomain: 29,
|
|
563
|
+
usdc: "0xa00C59fF5a080D2b954d0c75e46E22a0c371235a",
|
|
564
|
+
usdcDecimals: 6,
|
|
565
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
566
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
567
|
+
nativeCurrency: { symbol: "INJ", decimals: 18 },
|
|
568
|
+
defaultRpcUrls: [
|
|
569
|
+
"https://sentry.evm-rpc.injective.network",
|
|
570
|
+
"https://evm.archival.chain.virtual.json-rpc.injective.network"
|
|
571
|
+
],
|
|
572
|
+
explorerUrl: "https://blockscout.injective.network",
|
|
573
|
+
capabilities: {
|
|
574
|
+
fastSource: false,
|
|
575
|
+
forwardingDestination: false,
|
|
576
|
+
upfrontFeeSource: false,
|
|
577
|
+
standardFeeSwitch: true
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
var Morph = {
|
|
581
|
+
id: "Morph",
|
|
582
|
+
label: "Morph",
|
|
583
|
+
ecosystem: "evm",
|
|
584
|
+
network: "mainnet",
|
|
585
|
+
evmChainId: 2818,
|
|
586
|
+
cctpDomain: 30,
|
|
587
|
+
usdc: "0xCfb1186F4e93D60E60a8bDd997427D1F33bc372B",
|
|
588
|
+
usdcDecimals: 6,
|
|
589
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
590
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
591
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
592
|
+
defaultRpcUrls: ["https://rpc-bgw.morph.network"],
|
|
593
|
+
explorerUrl: "https://explorer.morph.network",
|
|
594
|
+
capabilities: {
|
|
595
|
+
fastSource: true,
|
|
596
|
+
forwardingDestination: false,
|
|
597
|
+
upfrontFeeSource: false,
|
|
598
|
+
standardFeeSwitch: true
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
var Pharos = {
|
|
602
|
+
id: "Pharos",
|
|
603
|
+
label: "Pharos",
|
|
604
|
+
ecosystem: "evm",
|
|
605
|
+
network: "mainnet",
|
|
606
|
+
evmChainId: 1672,
|
|
607
|
+
cctpDomain: 31,
|
|
608
|
+
usdc: "0xC879C018dB60520F4355C26eD1a6D572cdAC1815",
|
|
609
|
+
usdcDecimals: 6,
|
|
610
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
611
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
612
|
+
nativeCurrency: { symbol: "PROS", decimals: 18 },
|
|
613
|
+
defaultRpcUrls: ["https://rpc.pharos.xyz"],
|
|
614
|
+
explorerUrl: "https://www.pharosscan.xyz",
|
|
615
|
+
capabilities: {
|
|
616
|
+
fastSource: false,
|
|
617
|
+
forwardingDestination: false,
|
|
618
|
+
upfrontFeeSource: false,
|
|
619
|
+
standardFeeSwitch: true
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var Cronos = {
|
|
623
|
+
id: "Cronos",
|
|
624
|
+
label: "Cronos",
|
|
625
|
+
ecosystem: "evm",
|
|
626
|
+
network: "mainnet",
|
|
627
|
+
evmChainId: 25,
|
|
628
|
+
cctpDomain: 32,
|
|
629
|
+
usdc: "0x3D7F2C478aAfdB65542BCB44bCeeC05849999d2D",
|
|
630
|
+
usdcDecimals: 6,
|
|
631
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
632
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
633
|
+
nativeCurrency: { symbol: "CRO", decimals: 18 },
|
|
634
|
+
defaultRpcUrls: ["https://evm.cronos.com"],
|
|
635
|
+
explorerUrl: "https://explorer.cronos.com",
|
|
636
|
+
capabilities: {
|
|
637
|
+
fastSource: false,
|
|
638
|
+
forwardingDestination: false,
|
|
639
|
+
upfrontFeeSource: false,
|
|
640
|
+
standardFeeSwitch: true
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
var Plasma = {
|
|
644
|
+
id: "Plasma",
|
|
645
|
+
label: "Plasma",
|
|
646
|
+
ecosystem: "evm",
|
|
647
|
+
network: "mainnet",
|
|
648
|
+
evmChainId: 9745,
|
|
649
|
+
cctpDomain: 33,
|
|
650
|
+
usdc: "0x2d661C89D812261039AF9764eceaAee884f5F67F",
|
|
651
|
+
usdcDecimals: 6,
|
|
652
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
653
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
654
|
+
nativeCurrency: { symbol: "XPL", decimals: 18 },
|
|
655
|
+
defaultRpcUrls: ["https://rpc.plasma.to"],
|
|
656
|
+
explorerUrl: "https://plasmascan.to",
|
|
657
|
+
capabilities: {
|
|
658
|
+
fastSource: false,
|
|
659
|
+
forwardingDestination: false,
|
|
660
|
+
upfrontFeeSource: false,
|
|
661
|
+
standardFeeSwitch: true
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
var XLayer = {
|
|
665
|
+
id: "X_Layer",
|
|
666
|
+
label: "X Layer",
|
|
667
|
+
ecosystem: "evm",
|
|
668
|
+
network: "mainnet",
|
|
669
|
+
evmChainId: 196,
|
|
670
|
+
cctpDomain: 37,
|
|
671
|
+
usdc: "0xB6CEceAB302E2E4948951eE7843FC24E92933061",
|
|
672
|
+
usdcDecimals: 6,
|
|
673
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_MAINNET,
|
|
674
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_MAINNET,
|
|
675
|
+
nativeCurrency: { symbol: "OKB", decimals: 18 },
|
|
676
|
+
defaultRpcUrls: ["https://rpc.xlayer.tech", "https://xlayerrpc.okx.com"],
|
|
677
|
+
explorerUrl: "https://www.okx.com/web3/explorer/xlayer",
|
|
678
|
+
capabilities: {
|
|
679
|
+
fastSource: true,
|
|
680
|
+
forwardingDestination: false,
|
|
681
|
+
upfrontFeeSource: false,
|
|
682
|
+
standardFeeSwitch: true
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
var EVM_MAINNET_CHAINS = [
|
|
686
|
+
Ethereum,
|
|
687
|
+
Avalanche,
|
|
688
|
+
Optimism,
|
|
689
|
+
Arbitrum,
|
|
690
|
+
Base,
|
|
691
|
+
Polygon,
|
|
692
|
+
Unichain,
|
|
693
|
+
Linea,
|
|
694
|
+
Codex,
|
|
695
|
+
Sonic,
|
|
696
|
+
WorldChain,
|
|
697
|
+
Monad,
|
|
698
|
+
Sei,
|
|
699
|
+
XDC,
|
|
700
|
+
HyperEVM,
|
|
701
|
+
Ink,
|
|
702
|
+
Plume,
|
|
703
|
+
Arc,
|
|
704
|
+
Edge,
|
|
705
|
+
Injective,
|
|
706
|
+
Morph,
|
|
707
|
+
Pharos,
|
|
708
|
+
Cronos,
|
|
709
|
+
Plasma,
|
|
710
|
+
XLayer
|
|
711
|
+
];
|
|
712
|
+
|
|
713
|
+
// src/chains/testnet.ts
|
|
714
|
+
var EthereumSepolia = {
|
|
715
|
+
id: "Ethereum_Sepolia",
|
|
716
|
+
label: "Ethereum Sepolia",
|
|
717
|
+
ecosystem: "evm",
|
|
718
|
+
network: "testnet",
|
|
719
|
+
evmChainId: 11155111,
|
|
720
|
+
cctpDomain: 0,
|
|
721
|
+
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
722
|
+
usdcDecimals: 6,
|
|
723
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
724
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
725
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
726
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
727
|
+
defaultRpcUrls: ["https://ethereum-sepolia-rpc.publicnode.com"],
|
|
728
|
+
explorerUrl: "https://sepolia.etherscan.io",
|
|
729
|
+
capabilities: {
|
|
730
|
+
fastSource: true,
|
|
731
|
+
forwardingDestination: true,
|
|
732
|
+
upfrontFeeSource: true,
|
|
733
|
+
standardFeeSwitch: false
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
var AvalancheFuji = {
|
|
737
|
+
id: "Avalanche_Fuji",
|
|
738
|
+
label: "Avalanche Fuji",
|
|
739
|
+
ecosystem: "evm",
|
|
740
|
+
network: "testnet",
|
|
741
|
+
evmChainId: 43113,
|
|
742
|
+
cctpDomain: 1,
|
|
743
|
+
usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
|
|
744
|
+
usdcDecimals: 6,
|
|
745
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
746
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
747
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
748
|
+
nativeCurrency: { symbol: "AVAX", decimals: 18 },
|
|
749
|
+
defaultRpcUrls: ["https://api.avax-test.network/ext/bc/C/rpc"],
|
|
750
|
+
explorerUrl: "https://explorer-test.avax.network/c-chain",
|
|
751
|
+
capabilities: {
|
|
752
|
+
fastSource: false,
|
|
753
|
+
forwardingDestination: true,
|
|
754
|
+
upfrontFeeSource: true,
|
|
755
|
+
standardFeeSwitch: false
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
var OptimismSepolia = {
|
|
759
|
+
id: "Optimism_Sepolia",
|
|
760
|
+
label: "OP Sepolia",
|
|
761
|
+
ecosystem: "evm",
|
|
762
|
+
network: "testnet",
|
|
763
|
+
evmChainId: 11155420,
|
|
764
|
+
cctpDomain: 2,
|
|
765
|
+
usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
|
|
766
|
+
usdcDecimals: 6,
|
|
767
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
768
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
769
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
770
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
771
|
+
defaultRpcUrls: ["https://sepolia.optimism.io"],
|
|
772
|
+
explorerUrl: "https://testnet-explorer.optimism.io",
|
|
773
|
+
capabilities: {
|
|
774
|
+
fastSource: true,
|
|
775
|
+
forwardingDestination: true,
|
|
776
|
+
upfrontFeeSource: true,
|
|
777
|
+
standardFeeSwitch: false
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
var ArbitrumSepolia = {
|
|
781
|
+
id: "Arbitrum_Sepolia",
|
|
782
|
+
label: "Arbitrum Sepolia",
|
|
783
|
+
ecosystem: "evm",
|
|
784
|
+
network: "testnet",
|
|
785
|
+
evmChainId: 421614,
|
|
786
|
+
cctpDomain: 3,
|
|
787
|
+
usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
788
|
+
usdcDecimals: 6,
|
|
789
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
790
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
791
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
792
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
793
|
+
defaultRpcUrls: ["https://sepolia-rollup.arbitrum.io/rpc"],
|
|
794
|
+
explorerUrl: "https://sepolia.arbiscan.io",
|
|
795
|
+
capabilities: {
|
|
796
|
+
fastSource: true,
|
|
797
|
+
forwardingDestination: true,
|
|
798
|
+
upfrontFeeSource: true,
|
|
799
|
+
standardFeeSwitch: false
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
var BaseSepolia = {
|
|
803
|
+
id: "Base_Sepolia",
|
|
804
|
+
label: "Base Sepolia",
|
|
805
|
+
ecosystem: "evm",
|
|
806
|
+
network: "testnet",
|
|
807
|
+
evmChainId: 84532,
|
|
808
|
+
cctpDomain: 6,
|
|
809
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
810
|
+
usdcDecimals: 6,
|
|
811
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
812
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
813
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
814
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
815
|
+
defaultRpcUrls: ["https://sepolia.base.org"],
|
|
816
|
+
explorerUrl: "https://sepolia.basescan.org",
|
|
817
|
+
capabilities: {
|
|
818
|
+
fastSource: true,
|
|
819
|
+
forwardingDestination: true,
|
|
820
|
+
upfrontFeeSource: true,
|
|
821
|
+
standardFeeSwitch: false
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
var PolygonAmoyTestnet = {
|
|
825
|
+
id: "Polygon_Amoy_Testnet",
|
|
826
|
+
label: "Polygon PoS Amoy",
|
|
827
|
+
ecosystem: "evm",
|
|
828
|
+
network: "testnet",
|
|
829
|
+
evmChainId: 80002,
|
|
830
|
+
cctpDomain: 7,
|
|
831
|
+
usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
|
|
832
|
+
usdcDecimals: 6,
|
|
833
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
834
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
835
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
836
|
+
nativeCurrency: { symbol: "POL", decimals: 18 },
|
|
837
|
+
defaultRpcUrls: ["https://polygon-amoy.drpc.org"],
|
|
838
|
+
explorerUrl: "https://amoy.polygonscan.com",
|
|
839
|
+
capabilities: {
|
|
840
|
+
fastSource: false,
|
|
841
|
+
forwardingDestination: true,
|
|
842
|
+
upfrontFeeSource: true,
|
|
843
|
+
standardFeeSwitch: false
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
var UnichainSepolia = {
|
|
847
|
+
id: "Unichain_Sepolia",
|
|
848
|
+
label: "Unichain Sepolia",
|
|
849
|
+
ecosystem: "evm",
|
|
850
|
+
network: "testnet",
|
|
851
|
+
evmChainId: 1301,
|
|
852
|
+
cctpDomain: 10,
|
|
853
|
+
usdc: "0x31d0220469e10c4E71834a79b1f276d740d3768F",
|
|
854
|
+
usdcDecimals: 6,
|
|
855
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
856
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
857
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
858
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
859
|
+
defaultRpcUrls: ["https://sepolia.unichain.org"],
|
|
860
|
+
explorerUrl: "https://sepolia.uniscan.xyz",
|
|
861
|
+
capabilities: {
|
|
862
|
+
fastSource: true,
|
|
863
|
+
forwardingDestination: true,
|
|
864
|
+
upfrontFeeSource: true,
|
|
865
|
+
standardFeeSwitch: false
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
var LineaSepolia = {
|
|
869
|
+
id: "Linea_Sepolia",
|
|
870
|
+
label: "Linea Sepolia",
|
|
871
|
+
ecosystem: "evm",
|
|
872
|
+
network: "testnet",
|
|
873
|
+
evmChainId: 59141,
|
|
874
|
+
cctpDomain: 11,
|
|
875
|
+
usdc: "0xFEce4462D57bD51A6A552365A011b95f0E16d9B7",
|
|
876
|
+
usdcDecimals: 6,
|
|
877
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
878
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
879
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
880
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
881
|
+
defaultRpcUrls: ["https://rpc.sepolia.linea.build"],
|
|
882
|
+
explorerUrl: "https://sepolia.lineascan.build",
|
|
883
|
+
capabilities: {
|
|
884
|
+
fastSource: true,
|
|
885
|
+
forwardingDestination: true,
|
|
886
|
+
upfrontFeeSource: true,
|
|
887
|
+
standardFeeSwitch: false
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
var CodexTestnet = {
|
|
891
|
+
id: "Codex_Testnet",
|
|
892
|
+
label: "Codex Testnet",
|
|
893
|
+
ecosystem: "evm",
|
|
894
|
+
network: "testnet",
|
|
895
|
+
evmChainId: 812242,
|
|
896
|
+
cctpDomain: 12,
|
|
897
|
+
usdc: "0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f",
|
|
898
|
+
usdcDecimals: 6,
|
|
899
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
900
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
901
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
902
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
903
|
+
defaultRpcUrls: ["https://rpc.codex-stg.xyz"],
|
|
904
|
+
explorerUrl: "https://explorer.codex-stg.xyz",
|
|
905
|
+
capabilities: {
|
|
906
|
+
fastSource: true,
|
|
907
|
+
forwardingDestination: true,
|
|
908
|
+
upfrontFeeSource: true,
|
|
909
|
+
standardFeeSwitch: false
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
var SonicTestnet = {
|
|
913
|
+
id: "Sonic_Testnet",
|
|
914
|
+
label: "Sonic Testnet",
|
|
915
|
+
ecosystem: "evm",
|
|
916
|
+
network: "testnet",
|
|
917
|
+
evmChainId: 14601,
|
|
918
|
+
cctpDomain: 13,
|
|
919
|
+
usdc: "0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51",
|
|
920
|
+
usdcDecimals: 6,
|
|
921
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
922
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
923
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
924
|
+
nativeCurrency: { symbol: "S", decimals: 18 },
|
|
925
|
+
defaultRpcUrls: ["https://rpc.testnet.soniclabs.com"],
|
|
926
|
+
explorerUrl: "https://testnet.sonicscan.org",
|
|
927
|
+
capabilities: {
|
|
928
|
+
fastSource: false,
|
|
929
|
+
forwardingDestination: true,
|
|
930
|
+
upfrontFeeSource: true,
|
|
931
|
+
standardFeeSwitch: true
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
var WorldChainSepolia = {
|
|
935
|
+
id: "World_Chain_Sepolia",
|
|
936
|
+
label: "World Chain Sepolia",
|
|
937
|
+
ecosystem: "evm",
|
|
938
|
+
network: "testnet",
|
|
939
|
+
evmChainId: 4801,
|
|
940
|
+
cctpDomain: 14,
|
|
941
|
+
usdc: "0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88",
|
|
942
|
+
usdcDecimals: 6,
|
|
943
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
944
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
945
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
946
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
947
|
+
defaultRpcUrls: ["https://worldchain-sepolia.g.alchemy.com/public"],
|
|
948
|
+
explorerUrl: "https://worldchain-sepolia.explorer.alchemy.com",
|
|
949
|
+
capabilities: {
|
|
950
|
+
fastSource: true,
|
|
951
|
+
forwardingDestination: true,
|
|
952
|
+
upfrontFeeSource: true,
|
|
953
|
+
standardFeeSwitch: false
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
var MonadTestnet = {
|
|
957
|
+
id: "Monad_Testnet",
|
|
958
|
+
label: "Monad Testnet",
|
|
959
|
+
ecosystem: "evm",
|
|
960
|
+
network: "testnet",
|
|
961
|
+
evmChainId: 10143,
|
|
962
|
+
cctpDomain: 15,
|
|
963
|
+
usdc: "0x534b2f3A21130d7a60830c2Df862319e593943A3",
|
|
964
|
+
usdcDecimals: 6,
|
|
965
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
966
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
967
|
+
nativeCurrency: { symbol: "MON", decimals: 18 },
|
|
968
|
+
defaultRpcUrls: [
|
|
969
|
+
"https://testnet-rpc.monad.xyz",
|
|
970
|
+
"https://rpc-testnet.monadinfra.com"
|
|
971
|
+
],
|
|
972
|
+
explorerUrl: "https://testnet.monadvision.com",
|
|
973
|
+
capabilities: {
|
|
974
|
+
fastSource: false,
|
|
975
|
+
forwardingDestination: true,
|
|
976
|
+
upfrontFeeSource: false,
|
|
977
|
+
standardFeeSwitch: true
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
var SeiTestnet = {
|
|
981
|
+
id: "Sei_Testnet",
|
|
982
|
+
label: "Sei Testnet",
|
|
983
|
+
ecosystem: "evm",
|
|
984
|
+
network: "testnet",
|
|
985
|
+
evmChainId: 1328,
|
|
986
|
+
cctpDomain: 16,
|
|
987
|
+
usdc: "0x4fCF1784B31630811181f670Aea7A7bEF803eaED",
|
|
988
|
+
usdcDecimals: 6,
|
|
989
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
990
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
991
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
992
|
+
nativeCurrency: { symbol: "SEI", decimals: 18 },
|
|
993
|
+
defaultRpcUrls: ["https://evm-rpc-testnet.sei-apis.com"],
|
|
994
|
+
explorerUrl: "https://testnet.seiscan.io",
|
|
995
|
+
capabilities: {
|
|
996
|
+
fastSource: false,
|
|
997
|
+
forwardingDestination: true,
|
|
998
|
+
upfrontFeeSource: true,
|
|
999
|
+
standardFeeSwitch: true
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
var XDCApothem = {
|
|
1003
|
+
id: "XDC_Apothem",
|
|
1004
|
+
label: "XDC Apothem",
|
|
1005
|
+
ecosystem: "evm",
|
|
1006
|
+
network: "testnet",
|
|
1007
|
+
evmChainId: 51,
|
|
1008
|
+
cctpDomain: 18,
|
|
1009
|
+
usdc: "0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4",
|
|
1010
|
+
usdcDecimals: 6,
|
|
1011
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1012
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1013
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
1014
|
+
nativeCurrency: { symbol: "TXDC", decimals: 18 },
|
|
1015
|
+
defaultRpcUrls: [
|
|
1016
|
+
"https://rpc.apothem.network",
|
|
1017
|
+
"https://erpc.apothem.network"
|
|
1018
|
+
],
|
|
1019
|
+
explorerUrl: "https://testnet.xdcscan.com",
|
|
1020
|
+
capabilities: {
|
|
1021
|
+
fastSource: false,
|
|
1022
|
+
forwardingDestination: true,
|
|
1023
|
+
upfrontFeeSource: true,
|
|
1024
|
+
standardFeeSwitch: true
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
var HyperEVMTestnet = {
|
|
1028
|
+
id: "HyperEVM_Testnet",
|
|
1029
|
+
label: "HyperEVM Testnet",
|
|
1030
|
+
ecosystem: "evm",
|
|
1031
|
+
network: "testnet",
|
|
1032
|
+
evmChainId: 998,
|
|
1033
|
+
cctpDomain: 19,
|
|
1034
|
+
usdc: "0x2B3370eE501B4a559b57D449569354196457D8Ab",
|
|
1035
|
+
usdcDecimals: 6,
|
|
1036
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1037
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1038
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
1039
|
+
nativeCurrency: { symbol: "HYPE", decimals: 18 },
|
|
1040
|
+
defaultRpcUrls: ["https://rpc.hyperliquid-testnet.xyz/evm"],
|
|
1041
|
+
explorerUrl: "https://app.hyperliquid-testnet.xyz/explorer",
|
|
1042
|
+
capabilities: {
|
|
1043
|
+
fastSource: false,
|
|
1044
|
+
forwardingDestination: true,
|
|
1045
|
+
upfrontFeeSource: true,
|
|
1046
|
+
standardFeeSwitch: true
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
var InkTestnet = {
|
|
1050
|
+
id: "Ink_Testnet",
|
|
1051
|
+
label: "Ink Testnet",
|
|
1052
|
+
ecosystem: "evm",
|
|
1053
|
+
network: "testnet",
|
|
1054
|
+
evmChainId: 763373,
|
|
1055
|
+
cctpDomain: 21,
|
|
1056
|
+
usdc: "0xFabab97dCE620294D2B0b0e46C68964e326300Ac",
|
|
1057
|
+
usdcDecimals: 6,
|
|
1058
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1059
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1060
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
1061
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
1062
|
+
defaultRpcUrls: [
|
|
1063
|
+
"https://rpc-gel-sepolia.inkonchain.com",
|
|
1064
|
+
"https://rpc-qnd-sepolia.inkonchain.com"
|
|
1065
|
+
],
|
|
1066
|
+
explorerUrl: "https://explorer-sepolia.inkonchain.com",
|
|
1067
|
+
capabilities: {
|
|
1068
|
+
fastSource: true,
|
|
1069
|
+
forwardingDestination: true,
|
|
1070
|
+
upfrontFeeSource: true,
|
|
1071
|
+
standardFeeSwitch: true
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
var PlumeTestnet = {
|
|
1075
|
+
id: "Plume_Testnet",
|
|
1076
|
+
label: "Plume Testnet",
|
|
1077
|
+
ecosystem: "evm",
|
|
1078
|
+
network: "testnet",
|
|
1079
|
+
evmChainId: 98867,
|
|
1080
|
+
cctpDomain: 22,
|
|
1081
|
+
usdc: "0xcB5f30e335672893c7eb944B374c196392C19D18",
|
|
1082
|
+
usdcDecimals: 6,
|
|
1083
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1084
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1085
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
1086
|
+
nativeCurrency: { symbol: "PLUME", decimals: 18 },
|
|
1087
|
+
defaultRpcUrls: ["https://testnet-rpc.plume.org"],
|
|
1088
|
+
explorerUrl: "https://testnet-explorer.plume.org",
|
|
1089
|
+
capabilities: {
|
|
1090
|
+
fastSource: true,
|
|
1091
|
+
forwardingDestination: true,
|
|
1092
|
+
upfrontFeeSource: true,
|
|
1093
|
+
standardFeeSwitch: true
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
var ArcTestnet = {
|
|
1097
|
+
id: "Arc_Testnet",
|
|
1098
|
+
label: "Arc Testnet",
|
|
1099
|
+
ecosystem: "evm",
|
|
1100
|
+
network: "testnet",
|
|
1101
|
+
evmChainId: 5042002,
|
|
1102
|
+
cctpDomain: 26,
|
|
1103
|
+
usdc: "0x3600000000000000000000000000000000000000",
|
|
1104
|
+
usdcDecimals: 6,
|
|
1105
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1106
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1107
|
+
tokenMessengerWithFees: TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET,
|
|
1108
|
+
nativeCurrency: { symbol: "USDC", decimals: 18 },
|
|
1109
|
+
defaultRpcUrls: ["https://rpc.testnet.arc.io"],
|
|
1110
|
+
explorerUrl: "https://explorer.testnet.arc.io",
|
|
1111
|
+
capabilities: {
|
|
1112
|
+
fastSource: false,
|
|
1113
|
+
forwardingDestination: true,
|
|
1114
|
+
upfrontFeeSource: true,
|
|
1115
|
+
standardFeeSwitch: true
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
var EdgeTestnet = {
|
|
1119
|
+
id: "Edge_Testnet",
|
|
1120
|
+
label: "EDGE Testnet",
|
|
1121
|
+
ecosystem: "evm",
|
|
1122
|
+
network: "testnet",
|
|
1123
|
+
evmChainId: 33431,
|
|
1124
|
+
cctpDomain: 28,
|
|
1125
|
+
usdc: "0x2d9F7CAD728051AA35Ecdc472a14cf8cDF5CFD6B",
|
|
1126
|
+
usdcDecimals: 6,
|
|
1127
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1128
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1129
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
1130
|
+
defaultRpcUrls: ["https://edge-testnet.g.alchemy.com/public"],
|
|
1131
|
+
explorerUrl: "https://edge-testnet.explorer.alchemy.com",
|
|
1132
|
+
capabilities: {
|
|
1133
|
+
fastSource: true,
|
|
1134
|
+
forwardingDestination: true,
|
|
1135
|
+
upfrontFeeSource: false,
|
|
1136
|
+
standardFeeSwitch: true
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
var InjectiveTestnet = {
|
|
1140
|
+
id: "Injective_Testnet",
|
|
1141
|
+
label: "Injective Testnet",
|
|
1142
|
+
ecosystem: "evm",
|
|
1143
|
+
network: "testnet",
|
|
1144
|
+
evmChainId: 1439,
|
|
1145
|
+
cctpDomain: 29,
|
|
1146
|
+
usdc: "0x0C382e685bbeeFE5d3d9C29e29E341fEE8E84C5d",
|
|
1147
|
+
usdcDecimals: 6,
|
|
1148
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1149
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1150
|
+
nativeCurrency: { symbol: "INJ", decimals: 18 },
|
|
1151
|
+
defaultRpcUrls: [
|
|
1152
|
+
"https://k8s.testnet.json-rpc.injective.network",
|
|
1153
|
+
"https://testnet.evm.archival.chain.virtual.json-rpc.injective.network"
|
|
1154
|
+
],
|
|
1155
|
+
explorerUrl: "https://testnet.blockscout.injective.network",
|
|
1156
|
+
capabilities: {
|
|
1157
|
+
fastSource: false,
|
|
1158
|
+
forwardingDestination: false,
|
|
1159
|
+
upfrontFeeSource: false,
|
|
1160
|
+
standardFeeSwitch: true
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
var MorphTestnet = {
|
|
1164
|
+
id: "Morph_Testnet",
|
|
1165
|
+
label: "Morph Hoodi Testnet",
|
|
1166
|
+
ecosystem: "evm",
|
|
1167
|
+
network: "testnet",
|
|
1168
|
+
evmChainId: 2910,
|
|
1169
|
+
cctpDomain: 30,
|
|
1170
|
+
usdc: "0x7433b41C6c5e1d58D4Da99483609520255ab661B",
|
|
1171
|
+
usdcDecimals: 6,
|
|
1172
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1173
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1174
|
+
nativeCurrency: { symbol: "ETH", decimals: 18 },
|
|
1175
|
+
defaultRpcUrls: ["https://rpc-hoodi-bgw.morph.network"],
|
|
1176
|
+
explorerUrl: "https://explorer-hoodi.morph.network",
|
|
1177
|
+
capabilities: {
|
|
1178
|
+
fastSource: true,
|
|
1179
|
+
forwardingDestination: false,
|
|
1180
|
+
upfrontFeeSource: false,
|
|
1181
|
+
standardFeeSwitch: true
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
var PharosTestnet = {
|
|
1185
|
+
id: "Pharos_Testnet",
|
|
1186
|
+
label: "Pharos Testnet",
|
|
1187
|
+
ecosystem: "evm",
|
|
1188
|
+
network: "testnet",
|
|
1189
|
+
evmChainId: 688689,
|
|
1190
|
+
cctpDomain: 31,
|
|
1191
|
+
usdc: "0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B",
|
|
1192
|
+
usdcDecimals: 6,
|
|
1193
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1194
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1195
|
+
nativeCurrency: { symbol: "PHAROS", decimals: 18 },
|
|
1196
|
+
defaultRpcUrls: ["https://atlantic.dplabs-internal.com"],
|
|
1197
|
+
explorerUrl: "https://atlantic.pharosscan.xyz",
|
|
1198
|
+
capabilities: {
|
|
1199
|
+
fastSource: false,
|
|
1200
|
+
forwardingDestination: false,
|
|
1201
|
+
upfrontFeeSource: false,
|
|
1202
|
+
standardFeeSwitch: true
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
var CronosTestnet = {
|
|
1206
|
+
id: "Cronos_Testnet",
|
|
1207
|
+
label: "Cronos Testnet",
|
|
1208
|
+
ecosystem: "evm",
|
|
1209
|
+
network: "testnet",
|
|
1210
|
+
evmChainId: 338,
|
|
1211
|
+
cctpDomain: 32,
|
|
1212
|
+
usdc: "0xEb33dc5fac03833e132593659e1dE7256aB59794",
|
|
1213
|
+
usdcDecimals: 6,
|
|
1214
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1215
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1216
|
+
nativeCurrency: { symbol: "tCRO", decimals: 18 },
|
|
1217
|
+
defaultRpcUrls: ["https://evm-t3.cronos.com"],
|
|
1218
|
+
explorerUrl: "https://explorer.cronos.com/testnet",
|
|
1219
|
+
capabilities: {
|
|
1220
|
+
fastSource: false,
|
|
1221
|
+
forwardingDestination: false,
|
|
1222
|
+
upfrontFeeSource: false,
|
|
1223
|
+
standardFeeSwitch: true
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
var PlasmaTestnet = {
|
|
1227
|
+
id: "Plasma_Testnet",
|
|
1228
|
+
label: "Plasma Testnet",
|
|
1229
|
+
ecosystem: "evm",
|
|
1230
|
+
network: "testnet",
|
|
1231
|
+
evmChainId: 9746,
|
|
1232
|
+
cctpDomain: 33,
|
|
1233
|
+
usdc: "0xE67Fb267022cBA8064Dd388CC2FED724F3120D9D",
|
|
1234
|
+
usdcDecimals: 6,
|
|
1235
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1236
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1237
|
+
nativeCurrency: { symbol: "XPL", decimals: 18 },
|
|
1238
|
+
defaultRpcUrls: ["https://testnet-rpc.plasma.to"],
|
|
1239
|
+
explorerUrl: "https://testnet.plasmascan.to",
|
|
1240
|
+
capabilities: {
|
|
1241
|
+
fastSource: false,
|
|
1242
|
+
forwardingDestination: false,
|
|
1243
|
+
upfrontFeeSource: false,
|
|
1244
|
+
standardFeeSwitch: true
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
var XLayerTestnet = {
|
|
1248
|
+
id: "X_Layer_Testnet",
|
|
1249
|
+
label: "X Layer Testnet",
|
|
1250
|
+
ecosystem: "evm",
|
|
1251
|
+
network: "testnet",
|
|
1252
|
+
evmChainId: 1952,
|
|
1253
|
+
cctpDomain: 37,
|
|
1254
|
+
usdc: "0xDec90b78111Ba2fc6FC6d84d8B9ec159A2d4b9B3",
|
|
1255
|
+
usdcDecimals: 6,
|
|
1256
|
+
tokenMessenger: TOKEN_MESSENGER_V2_EVM_TESTNET,
|
|
1257
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_EVM_TESTNET,
|
|
1258
|
+
nativeCurrency: { symbol: "OKB", decimals: 18 },
|
|
1259
|
+
defaultRpcUrls: [
|
|
1260
|
+
"https://testrpc.xlayer.tech/terigon",
|
|
1261
|
+
"https://xlayertestrpc.okx.com/terigon"
|
|
1262
|
+
],
|
|
1263
|
+
explorerUrl: "https://www.okx.com/web3/explorer/xlayer-test",
|
|
1264
|
+
capabilities: {
|
|
1265
|
+
fastSource: true,
|
|
1266
|
+
forwardingDestination: false,
|
|
1267
|
+
upfrontFeeSource: false,
|
|
1268
|
+
standardFeeSwitch: true
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
var EVM_TESTNET_CHAINS = [
|
|
1272
|
+
EthereumSepolia,
|
|
1273
|
+
AvalancheFuji,
|
|
1274
|
+
OptimismSepolia,
|
|
1275
|
+
ArbitrumSepolia,
|
|
1276
|
+
BaseSepolia,
|
|
1277
|
+
PolygonAmoyTestnet,
|
|
1278
|
+
UnichainSepolia,
|
|
1279
|
+
LineaSepolia,
|
|
1280
|
+
CodexTestnet,
|
|
1281
|
+
SonicTestnet,
|
|
1282
|
+
WorldChainSepolia,
|
|
1283
|
+
MonadTestnet,
|
|
1284
|
+
SeiTestnet,
|
|
1285
|
+
XDCApothem,
|
|
1286
|
+
HyperEVMTestnet,
|
|
1287
|
+
InkTestnet,
|
|
1288
|
+
PlumeTestnet,
|
|
1289
|
+
ArcTestnet,
|
|
1290
|
+
EdgeTestnet,
|
|
1291
|
+
InjectiveTestnet,
|
|
1292
|
+
MorphTestnet,
|
|
1293
|
+
PharosTestnet,
|
|
1294
|
+
CronosTestnet,
|
|
1295
|
+
PlasmaTestnet,
|
|
1296
|
+
XLayerTestnet
|
|
1297
|
+
];
|
|
1298
|
+
|
|
1299
|
+
// src/chains/solana.ts
|
|
1300
|
+
var Solana = {
|
|
1301
|
+
id: "Solana",
|
|
1302
|
+
label: "Solana",
|
|
1303
|
+
ecosystem: "solana",
|
|
1304
|
+
network: "mainnet",
|
|
1305
|
+
cluster: "mainnet-beta",
|
|
1306
|
+
cctpDomain: SOLANA_CCTP_DOMAIN,
|
|
1307
|
+
usdc: USDC_SPL_MAINNET,
|
|
1308
|
+
usdcDecimals: 6,
|
|
1309
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_V2_SOLANA,
|
|
1310
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_SOLANA,
|
|
1311
|
+
nativeCurrency: { symbol: "SOL", decimals: 9 },
|
|
1312
|
+
defaultRpcUrls: [
|
|
1313
|
+
"https://api.mainnet.solana.com",
|
|
1314
|
+
"https://api.mainnet-beta.solana.com"
|
|
1315
|
+
],
|
|
1316
|
+
explorerUrl: "https://explorer.solana.com",
|
|
1317
|
+
capabilities: {
|
|
1318
|
+
fastSource: true,
|
|
1319
|
+
forwardingDestination: true,
|
|
1320
|
+
upfrontFeeSource: false,
|
|
1321
|
+
standardFeeSwitch: false
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
var SolanaDevnet = {
|
|
1325
|
+
id: "Solana_Devnet",
|
|
1326
|
+
label: "Solana Devnet",
|
|
1327
|
+
ecosystem: "solana",
|
|
1328
|
+
network: "testnet",
|
|
1329
|
+
cluster: "devnet",
|
|
1330
|
+
cctpDomain: SOLANA_CCTP_DOMAIN,
|
|
1331
|
+
usdc: USDC_SPL_DEVNET,
|
|
1332
|
+
usdcDecimals: 6,
|
|
1333
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_V2_SOLANA,
|
|
1334
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_SOLANA,
|
|
1335
|
+
nativeCurrency: { symbol: "SOL", decimals: 9 },
|
|
1336
|
+
defaultRpcUrls: ["https://api.devnet.solana.com"],
|
|
1337
|
+
explorerUrl: "https://explorer.solana.com",
|
|
1338
|
+
explorerQuery: "?cluster=devnet",
|
|
1339
|
+
capabilities: {
|
|
1340
|
+
fastSource: true,
|
|
1341
|
+
forwardingDestination: true,
|
|
1342
|
+
upfrontFeeSource: false,
|
|
1343
|
+
standardFeeSwitch: false
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
function deriveUsdcSac(issuer, passphrase) {
|
|
1347
|
+
return new Asset("USDC", issuer).contractId(passphrase);
|
|
1348
|
+
}
|
|
1349
|
+
var USDC_SAC_STELLAR_MAINNET = deriveUsdcSac(
|
|
1350
|
+
USDC_ISSUER_STELLAR_MAINNET,
|
|
1351
|
+
STELLAR_NETWORK_PASSPHRASE_MAINNET
|
|
1352
|
+
);
|
|
1353
|
+
var USDC_SAC_STELLAR_TESTNET = deriveUsdcSac(
|
|
1354
|
+
USDC_ISSUER_STELLAR_TESTNET,
|
|
1355
|
+
STELLAR_NETWORK_PASSPHRASE_TESTNET
|
|
1356
|
+
);
|
|
1357
|
+
var Stellar = {
|
|
1358
|
+
id: "Stellar",
|
|
1359
|
+
label: "Stellar",
|
|
1360
|
+
ecosystem: "stellar",
|
|
1361
|
+
network: "mainnet",
|
|
1362
|
+
stellarNetwork: "public",
|
|
1363
|
+
networkPassphrase: STELLAR_NETWORK_PASSPHRASE_MAINNET,
|
|
1364
|
+
cctpDomain: STELLAR_CCTP_DOMAIN,
|
|
1365
|
+
usdcIssuer: USDC_ISSUER_STELLAR_MAINNET,
|
|
1366
|
+
usdc: USDC_SAC_STELLAR_MAINNET,
|
|
1367
|
+
usdcDecimals: STELLAR_USDC_DECIMALS,
|
|
1368
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_STELLAR_MAINNET,
|
|
1369
|
+
messageTransmitter: MESSAGE_TRANSMITTER_STELLAR_MAINNET,
|
|
1370
|
+
cctpForwarder: CCTP_FORWARDER_STELLAR_MAINNET,
|
|
1371
|
+
nativeCurrency: { symbol: "XLM", decimals: 7 },
|
|
1372
|
+
defaultRpcUrls: [
|
|
1373
|
+
"https://soroban-rpc.mainnet.stellar.gateway.fm",
|
|
1374
|
+
"https://mainnet.sorobanrpc.com"
|
|
1375
|
+
],
|
|
1376
|
+
explorerUrl: "https://stellar.expert/explorer/public",
|
|
1377
|
+
capabilities: {
|
|
1378
|
+
fastSource: false,
|
|
1379
|
+
forwardingDestination: false,
|
|
1380
|
+
upfrontFeeSource: false,
|
|
1381
|
+
standardFeeSwitch: false
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
var StellarTestnet = {
|
|
1385
|
+
id: "Stellar_Testnet",
|
|
1386
|
+
label: "Stellar Testnet",
|
|
1387
|
+
ecosystem: "stellar",
|
|
1388
|
+
network: "testnet",
|
|
1389
|
+
stellarNetwork: "testnet",
|
|
1390
|
+
networkPassphrase: STELLAR_NETWORK_PASSPHRASE_TESTNET,
|
|
1391
|
+
cctpDomain: STELLAR_CCTP_DOMAIN,
|
|
1392
|
+
usdcIssuer: USDC_ISSUER_STELLAR_TESTNET,
|
|
1393
|
+
usdc: USDC_SAC_STELLAR_TESTNET,
|
|
1394
|
+
usdcDecimals: STELLAR_USDC_DECIMALS,
|
|
1395
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_STELLAR_TESTNET,
|
|
1396
|
+
messageTransmitter: MESSAGE_TRANSMITTER_STELLAR_TESTNET,
|
|
1397
|
+
cctpForwarder: CCTP_FORWARDER_STELLAR_TESTNET,
|
|
1398
|
+
nativeCurrency: { symbol: "XLM", decimals: 7 },
|
|
1399
|
+
defaultRpcUrls: ["https://soroban-testnet.stellar.org"],
|
|
1400
|
+
explorerUrl: "https://stellar.expert/explorer/testnet",
|
|
1401
|
+
capabilities: {
|
|
1402
|
+
fastSource: false,
|
|
1403
|
+
forwardingDestination: false,
|
|
1404
|
+
upfrontFeeSource: false,
|
|
1405
|
+
standardFeeSwitch: false
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
// src/chains/starknet.ts
|
|
1410
|
+
var Starknet = {
|
|
1411
|
+
id: "Starknet",
|
|
1412
|
+
label: "Starknet",
|
|
1413
|
+
ecosystem: "starknet",
|
|
1414
|
+
network: "mainnet",
|
|
1415
|
+
starknetNetwork: "mainnet",
|
|
1416
|
+
chainId: STARKNET_CHAIN_ID_MAINNET,
|
|
1417
|
+
cctpDomain: STARKNET_CCTP_DOMAIN,
|
|
1418
|
+
usdc: USDC_STARKNET_MAINNET,
|
|
1419
|
+
usdcDecimals: 6,
|
|
1420
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_V2_STARKNET_MAINNET,
|
|
1421
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_STARKNET_MAINNET,
|
|
1422
|
+
nativeCurrency: { symbol: "STRK", decimals: 18 },
|
|
1423
|
+
defaultRpcUrls: ["https://starknet-rpc.publicnode.com"],
|
|
1424
|
+
explorerUrl: "https://voyager.online",
|
|
1425
|
+
capabilities: {
|
|
1426
|
+
fastSource: true,
|
|
1427
|
+
forwardingDestination: false,
|
|
1428
|
+
upfrontFeeSource: false,
|
|
1429
|
+
standardFeeSwitch: false
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
var StarknetSepolia = {
|
|
1433
|
+
id: "Starknet_Sepolia",
|
|
1434
|
+
label: "Starknet Sepolia",
|
|
1435
|
+
ecosystem: "starknet",
|
|
1436
|
+
network: "testnet",
|
|
1437
|
+
starknetNetwork: "sepolia",
|
|
1438
|
+
chainId: STARKNET_CHAIN_ID_SEPOLIA,
|
|
1439
|
+
cctpDomain: STARKNET_CCTP_DOMAIN,
|
|
1440
|
+
usdc: USDC_STARKNET_SEPOLIA,
|
|
1441
|
+
usdcDecimals: 6,
|
|
1442
|
+
tokenMessengerMinter: TOKEN_MESSENGER_MINTER_V2_STARKNET_SEPOLIA,
|
|
1443
|
+
messageTransmitter: MESSAGE_TRANSMITTER_V2_STARKNET_SEPOLIA,
|
|
1444
|
+
nativeCurrency: { symbol: "STRK", decimals: 18 },
|
|
1445
|
+
defaultRpcUrls: ["https://starknet-sepolia-rpc.publicnode.com"],
|
|
1446
|
+
explorerUrl: "https://sepolia.voyager.online",
|
|
1447
|
+
capabilities: {
|
|
1448
|
+
fastSource: true,
|
|
1449
|
+
forwardingDestination: false,
|
|
1450
|
+
upfrontFeeSource: false,
|
|
1451
|
+
standardFeeSwitch: false
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
// src/chains/registry.ts
|
|
1456
|
+
var UnknownChainError = class extends InvalidInputError {
|
|
1457
|
+
constructor(detail) {
|
|
1458
|
+
super("invalid_chain", `Unknown chain: ${detail}`);
|
|
1459
|
+
this.name = "UnknownChainError";
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
var CHAINS = [
|
|
1463
|
+
...EVM_MAINNET_CHAINS,
|
|
1464
|
+
...EVM_TESTNET_CHAINS,
|
|
1465
|
+
Solana,
|
|
1466
|
+
SolanaDevnet,
|
|
1467
|
+
Stellar,
|
|
1468
|
+
StellarTestnet,
|
|
1469
|
+
Starknet,
|
|
1470
|
+
StarknetSepolia
|
|
1471
|
+
];
|
|
1472
|
+
var ALL = CHAINS;
|
|
1473
|
+
var BY_ID = new Map(ALL.map((c) => [c.id, c]));
|
|
1474
|
+
var BY_EVM_CHAIN_ID = new Map(
|
|
1475
|
+
ALL.filter(isEvmChain).map((c) => [c.evmChainId, c])
|
|
1476
|
+
);
|
|
1477
|
+
var BY_DOMAIN_AND_NETWORK = new Map(
|
|
1478
|
+
ALL.map((c) => [`${c.cctpDomain}:${c.network}`, c])
|
|
1479
|
+
);
|
|
1480
|
+
function isEvmChain(chain) {
|
|
1481
|
+
return chain.ecosystem === "evm";
|
|
1482
|
+
}
|
|
1483
|
+
function isSolanaChain(chain) {
|
|
1484
|
+
return chain.ecosystem === "solana";
|
|
1485
|
+
}
|
|
1486
|
+
function isStellarChain(chain) {
|
|
1487
|
+
return chain.ecosystem === "stellar";
|
|
1488
|
+
}
|
|
1489
|
+
function isStarknetChain(chain) {
|
|
1490
|
+
return chain.ecosystem === "starknet";
|
|
1491
|
+
}
|
|
1492
|
+
function isChainId(value) {
|
|
1493
|
+
return BY_ID.has(value);
|
|
1494
|
+
}
|
|
1495
|
+
function getChain(id) {
|
|
1496
|
+
const chain = BY_ID.get(id);
|
|
1497
|
+
if (!chain) throw new UnknownChainError(`id="${id}"`);
|
|
1498
|
+
return chain;
|
|
1499
|
+
}
|
|
1500
|
+
function chainsByNetwork(network) {
|
|
1501
|
+
return ALL.filter((c) => c.network === network);
|
|
1502
|
+
}
|
|
1503
|
+
function chainByEvmChainId(evmChainId) {
|
|
1504
|
+
const chain = BY_EVM_CHAIN_ID.get(evmChainId);
|
|
1505
|
+
if (!chain) throw new UnknownChainError(`evmChainId=${evmChainId}`);
|
|
1506
|
+
return chain;
|
|
1507
|
+
}
|
|
1508
|
+
function tryChainByEvmChainId(evmChainId) {
|
|
1509
|
+
return BY_EVM_CHAIN_ID.get(evmChainId);
|
|
1510
|
+
}
|
|
1511
|
+
function chainByDomain(domain, network) {
|
|
1512
|
+
const chain = BY_DOMAIN_AND_NETWORK.get(`${domain}:${network}`);
|
|
1513
|
+
if (!chain) {
|
|
1514
|
+
throw new UnknownChainError(`domain=${domain} network=${network}`);
|
|
1515
|
+
}
|
|
1516
|
+
return chain;
|
|
1517
|
+
}
|
|
1518
|
+
function explorerTxUrl(chain, txHash) {
|
|
1519
|
+
return `${chain.explorerUrl}/tx/${txHash}${chain.explorerQuery ?? ""}`;
|
|
1520
|
+
}
|
|
1521
|
+
function explorerAddressUrl(chain, address) {
|
|
1522
|
+
return `${chain.explorerUrl}/${addressPath(chain, address)}/${address}${chain.explorerQuery ?? ""}`;
|
|
1523
|
+
}
|
|
1524
|
+
function addressPath(chain, address) {
|
|
1525
|
+
switch (chain.ecosystem) {
|
|
1526
|
+
case "starknet":
|
|
1527
|
+
return "contract";
|
|
1528
|
+
case "stellar":
|
|
1529
|
+
return address.startsWith("C") ? "contract" : "account";
|
|
1530
|
+
default:
|
|
1531
|
+
return "address";
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// src/chains/attestation.ts
|
|
1536
|
+
var s = (min, max = min) => ({ min, max });
|
|
1537
|
+
var L1_FINALITY = s(900, 1140);
|
|
1538
|
+
var BY_DOMAIN = {
|
|
1539
|
+
0: { fast: s(20), standard: L1_FINALITY },
|
|
1540
|
+
1: { standard: s(8) },
|
|
1541
|
+
2: { fast: s(8), standard: L1_FINALITY },
|
|
1542
|
+
3: { fast: s(8), standard: L1_FINALITY },
|
|
1543
|
+
5: { fast: s(8), standard: s(25) },
|
|
1544
|
+
6: { fast: s(8), standard: L1_FINALITY },
|
|
1545
|
+
7: { standard: s(8) },
|
|
1546
|
+
10: { fast: s(8), standard: L1_FINALITY },
|
|
1547
|
+
11: { fast: s(8), standard: s(21600, 115200) },
|
|
1548
|
+
12: { fast: s(8), standard: L1_FINALITY },
|
|
1549
|
+
13: { standard: s(8) },
|
|
1550
|
+
14: { fast: s(8), standard: L1_FINALITY },
|
|
1551
|
+
15: { standard: s(5) },
|
|
1552
|
+
16: { standard: s(5) },
|
|
1553
|
+
18: { standard: s(10) },
|
|
1554
|
+
19: { standard: s(5) },
|
|
1555
|
+
21: { fast: s(8), standard: s(1800) },
|
|
1556
|
+
22: { fast: s(8), standard: L1_FINALITY },
|
|
1557
|
+
25: { fast: s(20), standard: s(7200, 14400) },
|
|
1558
|
+
26: { standard: s(0.5) },
|
|
1559
|
+
27: { standard: s(5) },
|
|
1560
|
+
28: { fast: s(8), standard: s(960, 1260) },
|
|
1561
|
+
29: { standard: s(0.65) },
|
|
1562
|
+
30: { fast: s(8), standard: s(1200, 1800) },
|
|
1563
|
+
31: { standard: s(7) },
|
|
1564
|
+
32: { standard: s(0.5) },
|
|
1565
|
+
33: { standard: s(1) },
|
|
1566
|
+
37: { fast: s(8), standard: L1_FINALITY }
|
|
1567
|
+
};
|
|
1568
|
+
function attestationSeconds(chain, speed) {
|
|
1569
|
+
const times = BY_DOMAIN[chain.cctpDomain]?.[speed];
|
|
1570
|
+
if (!times) {
|
|
1571
|
+
throw new ProtocolError(
|
|
1572
|
+
"no_route",
|
|
1573
|
+
`No ${speed} attestation time for ${chain.label}.`
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
return times;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
// src/abi/tokenMessengerV2.ts
|
|
1580
|
+
var tokenMessengerV2Abi = [
|
|
1581
|
+
{
|
|
1582
|
+
type: "function",
|
|
1583
|
+
name: "depositForBurn",
|
|
1584
|
+
stateMutability: "nonpayable",
|
|
1585
|
+
inputs: [
|
|
1586
|
+
{ name: "amount", type: "uint256" },
|
|
1587
|
+
{ name: "destinationDomain", type: "uint32" },
|
|
1588
|
+
{ name: "mintRecipient", type: "bytes32" },
|
|
1589
|
+
{ name: "burnToken", type: "address" },
|
|
1590
|
+
{ name: "destinationCaller", type: "bytes32" },
|
|
1591
|
+
{ name: "maxFee", type: "uint256" },
|
|
1592
|
+
{ name: "minFinalityThreshold", type: "uint32" }
|
|
1593
|
+
],
|
|
1594
|
+
outputs: []
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
type: "function",
|
|
1598
|
+
name: "depositForBurnWithHook",
|
|
1599
|
+
stateMutability: "nonpayable",
|
|
1600
|
+
inputs: [
|
|
1601
|
+
{ name: "amount", type: "uint256" },
|
|
1602
|
+
{ name: "destinationDomain", type: "uint32" },
|
|
1603
|
+
{ name: "mintRecipient", type: "bytes32" },
|
|
1604
|
+
{ name: "burnToken", type: "address" },
|
|
1605
|
+
{ name: "destinationCaller", type: "bytes32" },
|
|
1606
|
+
{ name: "maxFee", type: "uint256" },
|
|
1607
|
+
{ name: "minFinalityThreshold", type: "uint32" },
|
|
1608
|
+
{ name: "hookData", type: "bytes" }
|
|
1609
|
+
],
|
|
1610
|
+
outputs: []
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
type: "event",
|
|
1614
|
+
name: "DepositForBurn",
|
|
1615
|
+
anonymous: false,
|
|
1616
|
+
inputs: [
|
|
1617
|
+
{ name: "burnToken", type: "address", indexed: true },
|
|
1618
|
+
{ name: "amount", type: "uint256", indexed: false },
|
|
1619
|
+
{ name: "depositor", type: "address", indexed: true },
|
|
1620
|
+
{ name: "mintRecipient", type: "bytes32", indexed: false },
|
|
1621
|
+
{ name: "destinationDomain", type: "uint32", indexed: false },
|
|
1622
|
+
{ name: "destinationTokenMessenger", type: "bytes32", indexed: false },
|
|
1623
|
+
{ name: "destinationCaller", type: "bytes32", indexed: false },
|
|
1624
|
+
{ name: "maxFee", type: "uint256", indexed: false },
|
|
1625
|
+
{ name: "minFinalityThreshold", type: "uint32", indexed: false },
|
|
1626
|
+
{ name: "hookData", type: "bytes", indexed: false }
|
|
1627
|
+
]
|
|
1628
|
+
}
|
|
1629
|
+
];
|
|
1630
|
+
|
|
1631
|
+
// src/abi/messageTransmitterV2.ts
|
|
1632
|
+
var messageTransmitterV2Abi = [
|
|
1633
|
+
{
|
|
1634
|
+
type: "function",
|
|
1635
|
+
name: "receiveMessage",
|
|
1636
|
+
stateMutability: "nonpayable",
|
|
1637
|
+
inputs: [
|
|
1638
|
+
{ name: "message", type: "bytes" },
|
|
1639
|
+
{ name: "attestation", type: "bytes" }
|
|
1640
|
+
],
|
|
1641
|
+
outputs: [{ name: "success", type: "bool" }]
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
type: "function",
|
|
1645
|
+
name: "usedNonces",
|
|
1646
|
+
stateMutability: "view",
|
|
1647
|
+
inputs: [{ name: "nonce", type: "bytes32" }],
|
|
1648
|
+
outputs: [{ name: "isUsed", type: "uint256" }]
|
|
1649
|
+
},
|
|
1650
|
+
{
|
|
1651
|
+
type: "event",
|
|
1652
|
+
name: "MessageReceived",
|
|
1653
|
+
anonymous: false,
|
|
1654
|
+
inputs: [
|
|
1655
|
+
{ name: "caller", type: "address", indexed: true },
|
|
1656
|
+
{ name: "sourceDomain", type: "uint32", indexed: true },
|
|
1657
|
+
{ name: "nonce", type: "bytes32", indexed: true },
|
|
1658
|
+
{ name: "sender", type: "bytes32", indexed: false },
|
|
1659
|
+
{ name: "finalityThresholdExecuted", type: "uint32", indexed: false },
|
|
1660
|
+
{ name: "messageBody", type: "bytes", indexed: false }
|
|
1661
|
+
]
|
|
1662
|
+
},
|
|
1663
|
+
{
|
|
1664
|
+
type: "event",
|
|
1665
|
+
name: "MessageSent",
|
|
1666
|
+
anonymous: false,
|
|
1667
|
+
inputs: [{ name: "message", type: "bytes", indexed: false }]
|
|
1668
|
+
}
|
|
1669
|
+
];
|
|
1670
|
+
|
|
1671
|
+
// src/abi/usdc.ts
|
|
1672
|
+
var usdcAbi = [
|
|
1673
|
+
{
|
|
1674
|
+
type: "function",
|
|
1675
|
+
name: "balanceOf",
|
|
1676
|
+
stateMutability: "view",
|
|
1677
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1678
|
+
outputs: [{ name: "balance", type: "uint256" }]
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
type: "function",
|
|
1682
|
+
name: "allowance",
|
|
1683
|
+
stateMutability: "view",
|
|
1684
|
+
inputs: [
|
|
1685
|
+
{ name: "owner", type: "address" },
|
|
1686
|
+
{ name: "spender", type: "address" }
|
|
1687
|
+
],
|
|
1688
|
+
outputs: [{ name: "remaining", type: "uint256" }]
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
type: "function",
|
|
1692
|
+
name: "decimals",
|
|
1693
|
+
stateMutability: "view",
|
|
1694
|
+
inputs: [],
|
|
1695
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
type: "function",
|
|
1699
|
+
name: "approve",
|
|
1700
|
+
stateMutability: "nonpayable",
|
|
1701
|
+
inputs: [
|
|
1702
|
+
{ name: "spender", type: "address" },
|
|
1703
|
+
{ name: "value", type: "uint256" }
|
|
1704
|
+
],
|
|
1705
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
type: "function",
|
|
1709
|
+
name: "transfer",
|
|
1710
|
+
stateMutability: "nonpayable",
|
|
1711
|
+
inputs: [
|
|
1712
|
+
{ name: "to", type: "address" },
|
|
1713
|
+
{ name: "value", type: "uint256" }
|
|
1714
|
+
],
|
|
1715
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
type: "function",
|
|
1719
|
+
name: "nonces",
|
|
1720
|
+
stateMutability: "view",
|
|
1721
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
1722
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
type: "function",
|
|
1726
|
+
name: "DOMAIN_SEPARATOR",
|
|
1727
|
+
stateMutability: "view",
|
|
1728
|
+
inputs: [],
|
|
1729
|
+
outputs: [{ name: "", type: "bytes32" }]
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
type: "function",
|
|
1733
|
+
name: "permit",
|
|
1734
|
+
stateMutability: "nonpayable",
|
|
1735
|
+
inputs: [
|
|
1736
|
+
{ name: "owner", type: "address" },
|
|
1737
|
+
{ name: "spender", type: "address" },
|
|
1738
|
+
{ name: "value", type: "uint256" },
|
|
1739
|
+
{ name: "deadline", type: "uint256" },
|
|
1740
|
+
{ name: "v", type: "uint8" },
|
|
1741
|
+
{ name: "r", type: "bytes32" },
|
|
1742
|
+
{ name: "s", type: "bytes32" }
|
|
1743
|
+
],
|
|
1744
|
+
outputs: []
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
type: "event",
|
|
1748
|
+
name: "Transfer",
|
|
1749
|
+
anonymous: false,
|
|
1750
|
+
inputs: [
|
|
1751
|
+
{ name: "from", type: "address", indexed: true },
|
|
1752
|
+
{ name: "to", type: "address", indexed: true },
|
|
1753
|
+
{ name: "value", type: "uint256", indexed: false }
|
|
1754
|
+
]
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
type: "event",
|
|
1758
|
+
name: "Approval",
|
|
1759
|
+
anonymous: false,
|
|
1760
|
+
inputs: [
|
|
1761
|
+
{ name: "owner", type: "address", indexed: true },
|
|
1762
|
+
{ name: "spender", type: "address", indexed: true },
|
|
1763
|
+
{ name: "value", type: "uint256", indexed: false }
|
|
1764
|
+
]
|
|
1765
|
+
}
|
|
1766
|
+
];
|
|
1767
|
+
|
|
1768
|
+
// src/abi/tokenMessengerWithFees.ts
|
|
1769
|
+
var tokenMessengerWithFeesAbi = [
|
|
1770
|
+
{
|
|
1771
|
+
type: "function",
|
|
1772
|
+
name: "depositForBurnWithFees",
|
|
1773
|
+
stateMutability: "payable",
|
|
1774
|
+
inputs: [
|
|
1775
|
+
{ name: "amount", type: "uint256" },
|
|
1776
|
+
{ name: "destinationDomain", type: "uint32" },
|
|
1777
|
+
{ name: "mintRecipient", type: "bytes32" },
|
|
1778
|
+
{ name: "burnToken", type: "address" },
|
|
1779
|
+
{ name: "destinationCaller", type: "bytes32" },
|
|
1780
|
+
{
|
|
1781
|
+
name: "claim",
|
|
1782
|
+
type: "tuple",
|
|
1783
|
+
components: [
|
|
1784
|
+
{ name: "signedQuote", type: "bytes" },
|
|
1785
|
+
{ name: "refundAddress", type: "address" }
|
|
1786
|
+
]
|
|
1787
|
+
}
|
|
1788
|
+
],
|
|
1789
|
+
outputs: []
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
type: "function",
|
|
1793
|
+
name: "getFee",
|
|
1794
|
+
stateMutability: "pure",
|
|
1795
|
+
inputs: [{ name: "signedQuote", type: "bytes" }],
|
|
1796
|
+
outputs: [
|
|
1797
|
+
{ name: "totalFee", type: "uint256" },
|
|
1798
|
+
{ name: "feeToken", type: "address" }
|
|
1799
|
+
]
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
type: "function",
|
|
1803
|
+
name: "tokenMessenger",
|
|
1804
|
+
stateMutability: "view",
|
|
1805
|
+
inputs: [],
|
|
1806
|
+
outputs: [{ name: "", type: "address" }]
|
|
1807
|
+
}
|
|
1808
|
+
];
|
|
1809
|
+
|
|
1810
|
+
// src/iris/urls.ts
|
|
1811
|
+
var IRIS_MAINNET_URL = "https://iris-api.circle.com";
|
|
1812
|
+
var IRIS_SANDBOX_URL = "https://iris-api-sandbox.circle.com";
|
|
1813
|
+
function irisBaseUrl(network) {
|
|
1814
|
+
return network === "mainnet" ? IRIS_MAINNET_URL : IRIS_SANDBOX_URL;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// src/iris/limiter.ts
|
|
1818
|
+
function createIrisLimiter(options = {}) {
|
|
1819
|
+
const rate = options.ratePerSecond ?? 30;
|
|
1820
|
+
const standDownMs = options.standDownMs ?? 5 * 6e4;
|
|
1821
|
+
const now = options.now ?? (() => Date.now());
|
|
1822
|
+
const sleep3 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1823
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1824
|
+
const bucket = (host) => {
|
|
1825
|
+
let b = buckets.get(host);
|
|
1826
|
+
if (!b) {
|
|
1827
|
+
b = { tokens: rate, updatedAt: now(), blockedUntil: 0 };
|
|
1828
|
+
buckets.set(host, b);
|
|
1829
|
+
}
|
|
1830
|
+
return b;
|
|
1831
|
+
};
|
|
1832
|
+
return {
|
|
1833
|
+
async acquire(host) {
|
|
1834
|
+
const b = bucket(host);
|
|
1835
|
+
if (now() < b.blockedUntil) {
|
|
1836
|
+
throw new IrisRateLimitedError(host, b.blockedUntil);
|
|
1837
|
+
}
|
|
1838
|
+
const elapsed = (now() - b.updatedAt) / 1e3;
|
|
1839
|
+
b.tokens = Math.min(rate, b.tokens + elapsed * rate);
|
|
1840
|
+
b.updatedAt = now();
|
|
1841
|
+
if (b.tokens < 1) {
|
|
1842
|
+
const waitMs = Math.ceil((1 - b.tokens) / rate * 1e3);
|
|
1843
|
+
await sleep3(waitMs);
|
|
1844
|
+
b.tokens += waitMs / 1e3 * rate;
|
|
1845
|
+
b.updatedAt = now();
|
|
1846
|
+
}
|
|
1847
|
+
b.tokens -= 1;
|
|
1848
|
+
},
|
|
1849
|
+
standDown(host) {
|
|
1850
|
+
const b = bucket(host);
|
|
1851
|
+
b.blockedUntil = now() + standDownMs;
|
|
1852
|
+
return b.blockedUntil;
|
|
1853
|
+
},
|
|
1854
|
+
now,
|
|
1855
|
+
sleep: sleep3
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
var defaultIrisLimiter = createIrisLimiter();
|
|
1859
|
+
|
|
1860
|
+
// src/iris/fetch.ts
|
|
1861
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1862
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
1863
|
+
var DEFAULT_BASE_DELAY_MS = 300;
|
|
1864
|
+
async function fetchIrisJson(url, options = {}, request = {
|
|
1865
|
+
method: "GET"
|
|
1866
|
+
}) {
|
|
1867
|
+
const {
|
|
1868
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
1869
|
+
maxRetries = DEFAULT_MAX_RETRIES,
|
|
1870
|
+
baseDelayMs = DEFAULT_BASE_DELAY_MS,
|
|
1871
|
+
signal,
|
|
1872
|
+
fetch: fetchImpl = fetch,
|
|
1873
|
+
limiter = defaultIrisLimiter
|
|
1874
|
+
} = options;
|
|
1875
|
+
const host = new URL(url).host;
|
|
1876
|
+
let lastError;
|
|
1877
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1878
|
+
if (signal?.aborted) throw new IrisAbortedError("Aborted by caller");
|
|
1879
|
+
await limiter.acquire(host);
|
|
1880
|
+
const ctrl = new AbortController();
|
|
1881
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
1882
|
+
const onCallerAbort = () => ctrl.abort();
|
|
1883
|
+
signal?.addEventListener("abort", onCallerAbort);
|
|
1884
|
+
try {
|
|
1885
|
+
const res = await fetchImpl(url, {
|
|
1886
|
+
method: request.method,
|
|
1887
|
+
headers: {
|
|
1888
|
+
accept: "application/json",
|
|
1889
|
+
...request.method === "POST" && {
|
|
1890
|
+
"content-type": "application/json"
|
|
1891
|
+
}
|
|
1892
|
+
},
|
|
1893
|
+
...request.method === "POST" && {
|
|
1894
|
+
body: JSON.stringify(request.body)
|
|
1895
|
+
},
|
|
1896
|
+
signal: ctrl.signal
|
|
1897
|
+
});
|
|
1898
|
+
if (res.ok) {
|
|
1899
|
+
let json;
|
|
1900
|
+
try {
|
|
1901
|
+
json = await res.json();
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
throw new IrisParseError(`Invalid JSON from ${url}`, err);
|
|
1904
|
+
}
|
|
1905
|
+
return json;
|
|
1906
|
+
}
|
|
1907
|
+
if (res.status === 429) {
|
|
1908
|
+
throw new IrisRateLimitedError(host, limiter.standDown(host));
|
|
1909
|
+
}
|
|
1910
|
+
if (res.status >= 400 && res.status < 500 && res.status !== 429) {
|
|
1911
|
+
throw new IrisHttpError(res.status, url, await safeText(res));
|
|
1912
|
+
}
|
|
1913
|
+
lastError = new IrisHttpError(res.status, url, await safeText(res));
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
if (err instanceof IrisParseError) throw err;
|
|
1916
|
+
if (err instanceof IrisRateLimitedError) throw err;
|
|
1917
|
+
if (err instanceof IrisHttpError) {
|
|
1918
|
+
if (err.status < 500 && err.status !== 429) throw err;
|
|
1919
|
+
}
|
|
1920
|
+
if (signal?.aborted) throw new IrisAbortedError("Aborted by caller");
|
|
1921
|
+
lastError = err;
|
|
1922
|
+
} finally {
|
|
1923
|
+
clearTimeout(timer);
|
|
1924
|
+
signal?.removeEventListener("abort", onCallerAbort);
|
|
1925
|
+
}
|
|
1926
|
+
if (attempt < maxRetries) {
|
|
1927
|
+
await sleep(baseDelayMs * (attempt + 1), signal);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
if (lastError instanceof IrisHttpError) throw lastError;
|
|
1931
|
+
throw new IrisNetworkError(
|
|
1932
|
+
`Iris request failed after ${maxRetries + 1} attempts`,
|
|
1933
|
+
lastError
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
async function safeText(res) {
|
|
1937
|
+
try {
|
|
1938
|
+
return await res.text();
|
|
1939
|
+
} catch {
|
|
1940
|
+
return "";
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
function sleep(ms, signal) {
|
|
1944
|
+
return new Promise((resolve, reject) => {
|
|
1945
|
+
if (signal?.aborted) {
|
|
1946
|
+
reject(new IrisAbortedError("Aborted by caller"));
|
|
1947
|
+
return;
|
|
1948
|
+
}
|
|
1949
|
+
const timer = setTimeout(() => {
|
|
1950
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1951
|
+
resolve();
|
|
1952
|
+
}, ms);
|
|
1953
|
+
function onAbort() {
|
|
1954
|
+
clearTimeout(timer);
|
|
1955
|
+
reject(new IrisAbortedError("Aborted by caller"));
|
|
1956
|
+
}
|
|
1957
|
+
signal?.addEventListener("abort", onAbort);
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/iris/types.ts
|
|
1962
|
+
var FINALITY_FAST = 1e3;
|
|
1963
|
+
var FINALITY_STANDARD = 2e3;
|
|
1964
|
+
|
|
1965
|
+
// src/fees/math.ts
|
|
1966
|
+
var CENTI_BPS_PER_UNIT = 1000000n;
|
|
1967
|
+
var DECIMAL = /^(\d+)(?:\.(\d+))?$/;
|
|
1968
|
+
function ceilDecimal(raw, decimals, what) {
|
|
1969
|
+
const text = typeof raw === "number" ? String(raw) : typeof raw === "string" ? raw : "";
|
|
1970
|
+
const match = DECIMAL.exec(text);
|
|
1971
|
+
if (!match) {
|
|
1972
|
+
throw new IrisParseError(`Iris ${what} is invalid: ${String(raw)}`);
|
|
1973
|
+
}
|
|
1974
|
+
const fraction = match[2] ?? "";
|
|
1975
|
+
const kept = fraction.slice(0, decimals).padEnd(decimals, "0");
|
|
1976
|
+
const dropped = /[1-9]/.test(fraction.slice(decimals)) ? 1n : 0n;
|
|
1977
|
+
return BigInt((match[1] ?? "0") + kept) + dropped;
|
|
1978
|
+
}
|
|
1979
|
+
function parseFeeBps(raw) {
|
|
1980
|
+
return ceilDecimal(raw, 2, "minimumFee");
|
|
1981
|
+
}
|
|
1982
|
+
function parseForwarderFee(raw) {
|
|
1983
|
+
return ceilDecimal(raw, 0, "forwardFee");
|
|
1984
|
+
}
|
|
1985
|
+
function computeBurnFee(amount, feeCentiBps) {
|
|
1986
|
+
if (amount < 0n) {
|
|
1987
|
+
throw new InvalidInputError(
|
|
1988
|
+
"invalid_amount",
|
|
1989
|
+
`amount must be non-negative, got ${amount}`
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
if (feeCentiBps < 0n) {
|
|
1993
|
+
throw new InvalidInputError(
|
|
1994
|
+
"invalid_input",
|
|
1995
|
+
`feeCentiBps must be non-negative, got ${feeCentiBps}`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
return ceilDiv(amount * feeCentiBps, CENTI_BPS_PER_UNIT);
|
|
1999
|
+
}
|
|
2000
|
+
function selectForwarderFee(tier, level = "med") {
|
|
2001
|
+
if (!tier.forwardFee) {
|
|
2002
|
+
throw new ProtocolError(
|
|
2003
|
+
"fee_unavailable",
|
|
2004
|
+
"Tier has no forwardFee. Fetch burn fees with includeForwarderFee=true"
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
const value = tier.forwardFee[level];
|
|
2008
|
+
if (value < 0n) {
|
|
2009
|
+
throw new ProtocolError(
|
|
2010
|
+
"fee_unavailable",
|
|
2011
|
+
`Invalid forwardFee.${level}: ${value}`
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
return value;
|
|
2015
|
+
}
|
|
2016
|
+
function computeMaxFee(input) {
|
|
2017
|
+
const {
|
|
2018
|
+
amount,
|
|
2019
|
+
tier,
|
|
2020
|
+
forwarderLevel = "med",
|
|
2021
|
+
forwarderBufferBps = 0,
|
|
2022
|
+
includeForwarder = true,
|
|
2023
|
+
burnFeeBufferBps = 0,
|
|
2024
|
+
minBurnFee = 0n
|
|
2025
|
+
} = input;
|
|
2026
|
+
const irisFee = computeBurnFee(amount, tier.minimumFee);
|
|
2027
|
+
const burnFee = irisFee > minBurnFee ? irisFee : minBurnFee;
|
|
2028
|
+
const forwarderFee = includeForwarder ? applyBufferBps(
|
|
2029
|
+
selectForwarderFee(tier, forwarderLevel),
|
|
2030
|
+
forwarderBufferBps
|
|
2031
|
+
) : 0n;
|
|
2032
|
+
return {
|
|
2033
|
+
burnFee,
|
|
2034
|
+
forwarderFee,
|
|
2035
|
+
maxFee: applyBufferBps(burnFee, burnFeeBufferBps) + forwarderFee
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
function applyBufferBps(value, bps) {
|
|
2039
|
+
if (!Number.isSafeInteger(bps) || bps < 0) {
|
|
2040
|
+
throw new InvalidInputError(
|
|
2041
|
+
"invalid_input",
|
|
2042
|
+
`bufferBps must be a non-negative whole number, got ${bps}`
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
return ceilDiv(value * (10000n + BigInt(bps)), 10000n);
|
|
2046
|
+
}
|
|
2047
|
+
function ceilDiv(numerator, denominator) {
|
|
2048
|
+
return (numerator + denominator - 1n) / denominator;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// src/iris/fees.ts
|
|
2052
|
+
async function getBurnFees(network, sourceDomain, destinationDomain, options = {}) {
|
|
2053
|
+
const { includeForwarderFee = true, ...fetchOpts } = options;
|
|
2054
|
+
const base = irisBaseUrl(network);
|
|
2055
|
+
const url = new URL(
|
|
2056
|
+
`${base}/v2/burn/USDC/fees/${sourceDomain}/${destinationDomain}`
|
|
2057
|
+
);
|
|
2058
|
+
if (includeForwarderFee) url.searchParams.set("forward", "true");
|
|
2059
|
+
const raw = await fetchIrisJson(url.toString(), fetchOpts);
|
|
2060
|
+
if (!Array.isArray(raw)) {
|
|
2061
|
+
throw new IrisParseError("Iris burn-fees response is not an array");
|
|
2062
|
+
}
|
|
2063
|
+
return raw.map((row) => normalizeTier(row)).filter((t) => t !== null);
|
|
2064
|
+
}
|
|
2065
|
+
function normalizeTier(row) {
|
|
2066
|
+
if (!row || typeof row !== "object") {
|
|
2067
|
+
throw new IrisParseError("Iris fee tier is not an object");
|
|
2068
|
+
}
|
|
2069
|
+
const r = row;
|
|
2070
|
+
const threshold = r.finalityThreshold;
|
|
2071
|
+
if (threshold !== FINALITY_FAST && threshold !== FINALITY_STANDARD) {
|
|
2072
|
+
return null;
|
|
2073
|
+
}
|
|
2074
|
+
const out = {
|
|
2075
|
+
finalityThreshold: threshold,
|
|
2076
|
+
minimumFee: parseFeeBps(r.minimumFee)
|
|
2077
|
+
};
|
|
2078
|
+
if (r.forwardFee && typeof r.forwardFee === "object") {
|
|
2079
|
+
const ff = r.forwardFee;
|
|
2080
|
+
out.forwardFee = {
|
|
2081
|
+
low: parseForwarderFee(ff.low),
|
|
2082
|
+
med: parseForwarderFee(ff.med),
|
|
2083
|
+
high: parseForwarderFee(ff.high)
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
return out;
|
|
2087
|
+
}
|
|
2088
|
+
function pickFeeTier(tiers, speed) {
|
|
2089
|
+
const threshold = speed === "fast" ? FINALITY_FAST : FINALITY_STANDARD;
|
|
2090
|
+
const tier = tiers.find((t) => t.finalityThreshold === threshold);
|
|
2091
|
+
if (!tier) {
|
|
2092
|
+
throw new ProtocolError(
|
|
2093
|
+
"tier_not_found",
|
|
2094
|
+
`Iris returned no ${speed === "fast" ? "Fast" : "Standard"} Transfer fee for this route.`
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
return tier;
|
|
2098
|
+
}
|
|
2099
|
+
function pickForwarderFee(tier, level = "med") {
|
|
2100
|
+
return selectForwarderFee(tier, level);
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
// src/iris/messages.ts
|
|
2104
|
+
async function getMessages(query, options) {
|
|
2105
|
+
const { network, baseUrl, ...fetchOpts } = options;
|
|
2106
|
+
const base = (baseUrl ?? irisBaseUrl(network)).replace(/\/+$/, "");
|
|
2107
|
+
const url = new URL(`${base}/v2/messages/${query.sourceDomain}`);
|
|
2108
|
+
if (query.transactionHash) {
|
|
2109
|
+
url.searchParams.set("transactionHash", query.transactionHash);
|
|
2110
|
+
}
|
|
2111
|
+
if (query.nonce) {
|
|
2112
|
+
url.searchParams.set("nonce", query.nonce);
|
|
2113
|
+
}
|
|
2114
|
+
try {
|
|
2115
|
+
const json = await fetchIrisJson(
|
|
2116
|
+
url.toString(),
|
|
2117
|
+
fetchOpts
|
|
2118
|
+
);
|
|
2119
|
+
return json.messages ?? [];
|
|
2120
|
+
} catch (err) {
|
|
2121
|
+
if (err instanceof IrisHttpError && err.status === 404) {
|
|
2122
|
+
return [];
|
|
2123
|
+
}
|
|
2124
|
+
throw err;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
var DEFAULT_INTERVAL_MS = 3e3;
|
|
2128
|
+
var DEFAULT_TIMEOUT_MS2 = 15 * 60 * 1e3;
|
|
2129
|
+
var TIMEOUT_MARGIN_MS = 5 * 60 * 1e3;
|
|
2130
|
+
function pollBudget(eta) {
|
|
2131
|
+
const intervalMs = eta.max <= 60 ? 2e3 : eta.max <= 30 * 60 ? 1e4 : 3e4;
|
|
2132
|
+
return { intervalMs, timeoutMs: eta.max * 2 * 1e3 + TIMEOUT_MARGIN_MS };
|
|
2133
|
+
}
|
|
2134
|
+
function isForwardDone(message) {
|
|
2135
|
+
return (message.forwardState === "CONFIRMED" || message.forwardState === "COMPLETE") && !!message.forwardTxHash;
|
|
2136
|
+
}
|
|
2137
|
+
function defaultIsDone(message) {
|
|
2138
|
+
if (message.forwardState != null) {
|
|
2139
|
+
return isForwardDone(message) || message.forwardState === "FAILED";
|
|
2140
|
+
}
|
|
2141
|
+
return message.status === "complete";
|
|
2142
|
+
}
|
|
2143
|
+
async function pollMessages(query, options) {
|
|
2144
|
+
const {
|
|
2145
|
+
intervalMs = DEFAULT_INTERVAL_MS,
|
|
2146
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2,
|
|
2147
|
+
isDone = defaultIsDone,
|
|
2148
|
+
onRateLimited,
|
|
2149
|
+
signal,
|
|
2150
|
+
...rest
|
|
2151
|
+
} = options;
|
|
2152
|
+
const limiter = rest.limiter ?? defaultIrisLimiter;
|
|
2153
|
+
let deadline = limiter.now() + timeoutMs;
|
|
2154
|
+
let latest = null;
|
|
2155
|
+
while (limiter.now() < deadline) {
|
|
2156
|
+
if (signal?.aborted) throw new IrisAbortedError("Aborted by caller");
|
|
2157
|
+
let messages;
|
|
2158
|
+
try {
|
|
2159
|
+
messages = await getMessages(query, { ...rest, signal });
|
|
2160
|
+
} catch (err) {
|
|
2161
|
+
if (!(err instanceof IrisRateLimitedError)) throw err;
|
|
2162
|
+
onRateLimited?.(err.retryAt);
|
|
2163
|
+
const waitMs = Math.max(0, err.retryAt - limiter.now());
|
|
2164
|
+
deadline += waitMs;
|
|
2165
|
+
await limiter.sleep(waitMs);
|
|
2166
|
+
continue;
|
|
2167
|
+
}
|
|
2168
|
+
if (messages.length > 0) {
|
|
2169
|
+
const first = messages[0];
|
|
2170
|
+
if (first) {
|
|
2171
|
+
latest = first;
|
|
2172
|
+
if (isDone(first)) return first;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
await sleep2(intervalMs, signal);
|
|
2176
|
+
}
|
|
2177
|
+
return latest;
|
|
2178
|
+
}
|
|
2179
|
+
function sleep2(ms, signal) {
|
|
2180
|
+
return new Promise((resolve, reject) => {
|
|
2181
|
+
if (signal?.aborted) {
|
|
2182
|
+
reject(new IrisAbortedError("Aborted by caller"));
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
const timer = setTimeout(() => {
|
|
2186
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2187
|
+
resolve();
|
|
2188
|
+
}, ms);
|
|
2189
|
+
function onAbort() {
|
|
2190
|
+
clearTimeout(timer);
|
|
2191
|
+
reject(new IrisAbortedError("Aborted by caller"));
|
|
2192
|
+
}
|
|
2193
|
+
signal?.addEventListener("abort", onAbort);
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// src/iris/reattest.ts
|
|
2198
|
+
async function reattest(nonce, options) {
|
|
2199
|
+
const { network, ...fetchOpts } = options;
|
|
2200
|
+
const url = `${irisBaseUrl(network)}/v2/reattest/${nonce}`;
|
|
2201
|
+
let json;
|
|
2202
|
+
try {
|
|
2203
|
+
json = await fetchIrisJson(url, fetchOpts, {
|
|
2204
|
+
method: "POST",
|
|
2205
|
+
body: {}
|
|
2206
|
+
});
|
|
2207
|
+
} catch (err) {
|
|
2208
|
+
if (err instanceof IrisHttpError && err.status === 404) return null;
|
|
2209
|
+
throw err;
|
|
2210
|
+
}
|
|
2211
|
+
const acknowledged = typeof json === "object" && json !== null && "nonce" in json ? json.nonce : void 0;
|
|
2212
|
+
if (typeof acknowledged !== "string") {
|
|
2213
|
+
throw new IrisParseError("Iris reattest reply has no nonce");
|
|
2214
|
+
}
|
|
2215
|
+
return { nonce: acknowledged };
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// src/iris/quote.ts
|
|
2219
|
+
async function getSignedBurnQuote(network, sourceDomain, destinationDomain, request, options = {}) {
|
|
2220
|
+
const url = `${irisBaseUrl(network)}/v2/quote/burn/usdc/${sourceDomain}/${destinationDomain}`;
|
|
2221
|
+
const raw = await fetchIrisJson(url, options, {
|
|
2222
|
+
method: "POST",
|
|
2223
|
+
body: {
|
|
2224
|
+
amount: request.amount.toString(),
|
|
2225
|
+
feeToken: request.feeToken,
|
|
2226
|
+
requests: request.requests.map((type) => ({ type }))
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
const quote = parseQuote(raw);
|
|
2230
|
+
if (quote.burnAmount !== void 0 && quote.burnAmount !== request.amount) {
|
|
2231
|
+
throw new IrisParseError(
|
|
2232
|
+
`Iris quoted ${quote.burnAmount} subunits, not the ${request.amount} requested`
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
if (quote.feeToken.toLowerCase() !== request.feeToken.toLowerCase()) {
|
|
2236
|
+
throw new IrisParseError(
|
|
2237
|
+
`Iris priced the fee in ${quote.feeToken}, not ${request.feeToken}`
|
|
2238
|
+
);
|
|
2239
|
+
}
|
|
2240
|
+
const types = quote.items.map((i) => i.type).sort();
|
|
2241
|
+
if (types.join() !== [...request.requests].sort().join()) {
|
|
2242
|
+
throw new IrisParseError(
|
|
2243
|
+
`Iris priced ${types.join(", ")}, not ${request.requests.join(", ")}`
|
|
2244
|
+
);
|
|
2245
|
+
}
|
|
2246
|
+
return quote;
|
|
2247
|
+
}
|
|
2248
|
+
function parseQuote(raw) {
|
|
2249
|
+
const fail = (what) => {
|
|
2250
|
+
throw new IrisParseError(`Iris quote: ${what}`);
|
|
2251
|
+
};
|
|
2252
|
+
if (typeof raw !== "object" || raw === null) return fail("not an object");
|
|
2253
|
+
const r = raw;
|
|
2254
|
+
const signedQuote = r.signedQuote;
|
|
2255
|
+
if (typeof signedQuote !== "string" || !/^0x([0-9a-fA-F]{2})+$/.test(signedQuote)) {
|
|
2256
|
+
return fail("signedQuote is not hex bytes");
|
|
2257
|
+
}
|
|
2258
|
+
if (!Number.isSafeInteger(r.issuedAt))
|
|
2259
|
+
return fail("issuedAt is not a timestamp");
|
|
2260
|
+
const feeToken = r.feeToken;
|
|
2261
|
+
if (typeof feeToken !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(feeToken)) {
|
|
2262
|
+
return fail("feeToken is not an address");
|
|
2263
|
+
}
|
|
2264
|
+
const feeTotalAmount = subunits(r.feeTotalAmount) ?? fail("feeTotalAmount");
|
|
2265
|
+
const burnAmount = r.burnAmount === void 0 ? void 0 : subunits(r.burnAmount) ?? fail("burnAmount");
|
|
2266
|
+
if (!Array.isArray(r.items) || r.items.length === 0) return fail("no items");
|
|
2267
|
+
const items = r.items.map(
|
|
2268
|
+
(item) => {
|
|
2269
|
+
const i = item ?? {};
|
|
2270
|
+
if (i.type !== "FORWARD" && i.type !== "PRE_FINALITY") {
|
|
2271
|
+
return fail(`unknown item type ${String(i.type)}`);
|
|
2272
|
+
}
|
|
2273
|
+
return {
|
|
2274
|
+
type: i.type,
|
|
2275
|
+
amount: subunits(i.amount) ?? fail("item amount")
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
);
|
|
2279
|
+
if (items.reduce((sum, i) => sum + i.amount, 0n) !== feeTotalAmount) {
|
|
2280
|
+
return fail("items do not add up to feeTotalAmount");
|
|
2281
|
+
}
|
|
2282
|
+
return {
|
|
2283
|
+
signedQuote,
|
|
2284
|
+
issuedAt: r.issuedAt,
|
|
2285
|
+
expiry: parseExpiry(r.expiry) ?? fail("expiry"),
|
|
2286
|
+
feeTotalAmount,
|
|
2287
|
+
feeToken,
|
|
2288
|
+
...burnAmount !== void 0 && { burnAmount },
|
|
2289
|
+
items
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
function parseExpiry(raw) {
|
|
2293
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
2294
|
+
const e = raw;
|
|
2295
|
+
if (e.mode === "TIMESTAMP" && Number.isSafeInteger(e.expiresAt)) {
|
|
2296
|
+
return { mode: "TIMESTAMP", expiresAt: e.expiresAt };
|
|
2297
|
+
}
|
|
2298
|
+
if (e.mode === "BLOCK_NUMBER" && Number.isSafeInteger(e.expiresAtBlock)) {
|
|
2299
|
+
return {
|
|
2300
|
+
mode: "BLOCK_NUMBER",
|
|
2301
|
+
expiresAtBlock: BigInt(e.expiresAtBlock)
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
return void 0;
|
|
2305
|
+
}
|
|
2306
|
+
function subunits(raw) {
|
|
2307
|
+
return typeof raw === "string" && /^(0|[1-9]\d*)$/.test(raw) ? BigInt(raw) : void 0;
|
|
2308
|
+
}
|
|
2309
|
+
async function getFastBurnAllowance(network, options = {}) {
|
|
2310
|
+
const raw = await fetchIrisJson(
|
|
2311
|
+
`${irisBaseUrl(network)}/v2/fastBurn/USDC/allowance`,
|
|
2312
|
+
options
|
|
2313
|
+
);
|
|
2314
|
+
const value = typeof raw === "object" && raw !== null && "allowance" in raw ? raw.allowance : void 0;
|
|
2315
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
2316
|
+
throw new IrisParseError("Iris allowance is not a non-negative number");
|
|
2317
|
+
}
|
|
2318
|
+
const match = /^(\d+)(?:\.(\d+))?$/.exec(String(value));
|
|
2319
|
+
if (!match)
|
|
2320
|
+
throw new IrisParseError(`Iris allowance ${value} is not decimal`);
|
|
2321
|
+
const frac = (match[2] ?? "").padEnd(6, "0").slice(0, 6);
|
|
2322
|
+
return BigInt(match[1]) * 1000000n + BigInt(frac);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// src/rpc/jsonRpc.ts
|
|
2326
|
+
async function callRpc(chain, method, params, parse, options = {}, parseError) {
|
|
2327
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
2328
|
+
const body = JSON.stringify({ jsonrpc: "2.0", id: 1, method, params });
|
|
2329
|
+
const urls = [...options.rpcUrls ?? [], ...chain.defaultRpcUrls];
|
|
2330
|
+
const failures = [];
|
|
2331
|
+
for (let i = 0, behind = 0; i < urls.length; i++) {
|
|
2332
|
+
const url = urls[i];
|
|
2333
|
+
try {
|
|
2334
|
+
const res = await doFetch(url, {
|
|
2335
|
+
method: "POST",
|
|
2336
|
+
headers: { "content-type": "application/json" },
|
|
2337
|
+
body
|
|
2338
|
+
});
|
|
2339
|
+
if (!res.ok) {
|
|
2340
|
+
failures.push(`${url}: HTTP ${res.status}`);
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
const payload = await res.json();
|
|
2344
|
+
const result = typeof payload === "object" && payload !== null && "result" in payload ? parse(payload.result) : void 0;
|
|
2345
|
+
if (result !== void 0) return result;
|
|
2346
|
+
const answer = parseError?.(errorCode(payload));
|
|
2347
|
+
if (answer !== void 0) return answer;
|
|
2348
|
+
failures.push(`${url}: ${JSON.stringify(payload)}`);
|
|
2349
|
+
if (errorCode(payload) === LAGGING_BACKEND && behind < LAG_RETRIES) {
|
|
2350
|
+
behind += 1;
|
|
2351
|
+
await new Promise((resolve) => setTimeout(resolve, 250 * behind));
|
|
2352
|
+
i -= 1;
|
|
2353
|
+
continue;
|
|
2354
|
+
}
|
|
2355
|
+
behind = 0;
|
|
2356
|
+
} catch (err) {
|
|
2357
|
+
failures.push(
|
|
2358
|
+
`${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
throw new ProtocolError(
|
|
2363
|
+
"rpc_unavailable",
|
|
2364
|
+
`${method} on ${chain.id} failed: ${failures.join("; ")}`
|
|
2365
|
+
);
|
|
2366
|
+
}
|
|
2367
|
+
var LAGGING_BACKEND = -32014;
|
|
2368
|
+
var LAG_RETRIES = 3;
|
|
2369
|
+
function errorCode(payload) {
|
|
2370
|
+
if (typeof payload !== "object" || payload === null) return void 0;
|
|
2371
|
+
const error = payload.error;
|
|
2372
|
+
return error?.code;
|
|
2373
|
+
}
|
|
2374
|
+
async function getGasPrice(chain, options = {}) {
|
|
2375
|
+
return callRpc(
|
|
2376
|
+
chain,
|
|
2377
|
+
"eth_gasPrice",
|
|
2378
|
+
[],
|
|
2379
|
+
(r) => typeof r === "string" && /^0x[0-9a-fA-F]+$/.test(r) ? BigInt(r) : void 0,
|
|
2380
|
+
options
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
async function getBlockNumber(chain, options = {}) {
|
|
2384
|
+
return callRpc(
|
|
2385
|
+
chain,
|
|
2386
|
+
"eth_blockNumber",
|
|
2387
|
+
[],
|
|
2388
|
+
(r) => typeof r === "string" && /^0x[0-9a-fA-F]+$/.test(r) ? BigInt(r) : void 0,
|
|
2389
|
+
options
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
var USED_NONCES = "0xfeb61724";
|
|
2393
|
+
async function isNonceUsed(chain, nonce, options = {}) {
|
|
2394
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(nonce)) {
|
|
2395
|
+
throw new ProtocolError("rpc_unavailable", `nonce ${nonce} is not bytes32`);
|
|
2396
|
+
}
|
|
2397
|
+
return callRpc(
|
|
2398
|
+
chain,
|
|
2399
|
+
"eth_call",
|
|
2400
|
+
[
|
|
2401
|
+
{ to: chain.messageTransmitter, data: `${USED_NONCES}${nonce.slice(2)}` },
|
|
2402
|
+
"latest"
|
|
2403
|
+
],
|
|
2404
|
+
(r) => typeof r === "string" && /^0x[0-9a-fA-F]{64}$/.test(r) ? BigInt(r) !== 0n : void 0,
|
|
2405
|
+
options
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
async function getTransactionStatus(chain, hash2, options = {}) {
|
|
2409
|
+
return callRpc(
|
|
2410
|
+
chain,
|
|
2411
|
+
"eth_getTransactionReceipt",
|
|
2412
|
+
[hash2],
|
|
2413
|
+
(r) => {
|
|
2414
|
+
if (r === null) return "unknown";
|
|
2415
|
+
if (typeof r !== "object") return void 0;
|
|
2416
|
+
const status = r.status;
|
|
2417
|
+
if (status === "0x1") return "success";
|
|
2418
|
+
if (status === "0x0") return "reverted";
|
|
2419
|
+
return void 0;
|
|
2420
|
+
},
|
|
2421
|
+
options
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
var BALANCE_OF = "0x70a08231";
|
|
2425
|
+
async function getUsdcBalance(chain, owner, options = {}) {
|
|
2426
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(owner)) {
|
|
2427
|
+
throw new ProtocolError("rpc_unavailable", `${owner} is not an address`);
|
|
2428
|
+
}
|
|
2429
|
+
return callRpc(
|
|
2430
|
+
chain,
|
|
2431
|
+
"eth_call",
|
|
2432
|
+
[
|
|
2433
|
+
{
|
|
2434
|
+
to: chain.usdc,
|
|
2435
|
+
data: `${BALANCE_OF}${owner.slice(2).padStart(64, "0")}`
|
|
2436
|
+
},
|
|
2437
|
+
"latest"
|
|
2438
|
+
],
|
|
2439
|
+
(r) => typeof r === "string" && /^0x[0-9a-fA-F]{64}$/.test(r) ? BigInt(r) : void 0,
|
|
2440
|
+
options
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2443
|
+
var IS_NONCE_USED_SELECTOR = hash.getSelectorFromName("is_nonce_used");
|
|
2444
|
+
function bytes32(nonce) {
|
|
2445
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(nonce)) {
|
|
2446
|
+
throw new ProtocolError("rpc_unavailable", `nonce ${nonce} is not bytes32`);
|
|
2447
|
+
}
|
|
2448
|
+
return nonce.slice(2).toLowerCase();
|
|
2449
|
+
}
|
|
2450
|
+
async function isStarknetNonceUsed(chain, nonce, options = {}) {
|
|
2451
|
+
const hex = bytes32(nonce);
|
|
2452
|
+
return callRpc(
|
|
2453
|
+
chain,
|
|
2454
|
+
"starknet_call",
|
|
2455
|
+
{
|
|
2456
|
+
request: {
|
|
2457
|
+
contract_address: chain.messageTransmitter,
|
|
2458
|
+
entry_point_selector: IS_NONCE_USED_SELECTOR,
|
|
2459
|
+
// A u256 goes over the wire as its low then high 128 bits.
|
|
2460
|
+
calldata: [`0x${hex.slice(32)}`, `0x${hex.slice(0, 32)}`]
|
|
2461
|
+
},
|
|
2462
|
+
// Public Starknet RPCs reject the "pending" tag starknet.js defaults to.
|
|
2463
|
+
block_id: "latest"
|
|
2464
|
+
},
|
|
2465
|
+
(r) => Array.isArray(r) && (r[0] === "0x0" || r[0] === "0x1") ? r[0] === "0x1" : void 0,
|
|
2466
|
+
options
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
async function isStellarNonceUsed(chain, nonce, options = {}) {
|
|
2470
|
+
const tx = new TransactionBuilder(new Account(chain.usdcIssuer, "0"), {
|
|
2471
|
+
fee: BASE_FEE,
|
|
2472
|
+
networkPassphrase: chain.networkPassphrase
|
|
2473
|
+
}).addOperation(
|
|
2474
|
+
new Contract(chain.messageTransmitter).call(
|
|
2475
|
+
"is_nonce_used",
|
|
2476
|
+
nativeToScVal(Buffer.from(bytes32(nonce), "hex"), { type: "bytes" })
|
|
2477
|
+
)
|
|
2478
|
+
).setTimeout(30).build();
|
|
2479
|
+
return callRpc(
|
|
2480
|
+
chain,
|
|
2481
|
+
"simulateTransaction",
|
|
2482
|
+
{ transaction: tx.toXDR() },
|
|
2483
|
+
(r) => {
|
|
2484
|
+
const results = r?.results;
|
|
2485
|
+
const value = results?.[0]?.xdr;
|
|
2486
|
+
if (typeof value !== "string") return void 0;
|
|
2487
|
+
const used = scValToNative(xdr.ScVal.fromXDR(value, "base64"));
|
|
2488
|
+
return typeof used === "boolean" ? used : void 0;
|
|
2489
|
+
},
|
|
2490
|
+
options
|
|
2491
|
+
);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// src/rpc/starknetTx.ts
|
|
2495
|
+
var TXN_HASH_NOT_FOUND = 29;
|
|
2496
|
+
var ACCEPTED = /* @__PURE__ */ new Set(["ACCEPTED_ON_L2", "ACCEPTED_ON_L1"]);
|
|
2497
|
+
var NOT_YET = /* @__PURE__ */ new Set(["RECEIVED", "CANDIDATE", "PRE_CONFIRMED"]);
|
|
2498
|
+
async function getStarknetTransactionStatus(chain, hash2, options = {}) {
|
|
2499
|
+
return callRpc(
|
|
2500
|
+
chain,
|
|
2501
|
+
"starknet_getTransactionReceipt",
|
|
2502
|
+
{ transaction_hash: hash2 },
|
|
2503
|
+
(r) => {
|
|
2504
|
+
if (typeof r !== "object" || r === null) return void 0;
|
|
2505
|
+
const { finality_status, execution_status, block_number } = r;
|
|
2506
|
+
if (typeof finality_status !== "string") return void 0;
|
|
2507
|
+
if (NOT_YET.has(finality_status)) return { state: "pending" };
|
|
2508
|
+
if (!ACCEPTED.has(finality_status)) return void 0;
|
|
2509
|
+
if (typeof block_number !== "number") return void 0;
|
|
2510
|
+
if (execution_status === "SUCCEEDED") {
|
|
2511
|
+
return { state: "success", blockNumber: block_number };
|
|
2512
|
+
}
|
|
2513
|
+
if (execution_status === "REVERTED") {
|
|
2514
|
+
return { state: "reverted", blockNumber: block_number };
|
|
2515
|
+
}
|
|
2516
|
+
return void 0;
|
|
2517
|
+
},
|
|
2518
|
+
options,
|
|
2519
|
+
(code) => code === TXN_HASH_NOT_FOUND ? { state: "pending" } : void 0
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// src/forwarder/hookData.ts
|
|
2524
|
+
var CCTP_FORWARD_HOOK_DATA = "0x636374702d666f72776172640000000000000000000000000000000000000000";
|
|
2525
|
+
var CCTP_FORWARD_HOOK_DATA_LENGTH = 32;
|
|
2526
|
+
var MAGIC_PREFIX = "cctp-forward";
|
|
2527
|
+
var VERSION = 0;
|
|
2528
|
+
var PAYLOAD_LENGTH = 0;
|
|
2529
|
+
function buildForwarderHookData() {
|
|
2530
|
+
const buf = new Uint8Array(CCTP_FORWARD_HOOK_DATA_LENGTH);
|
|
2531
|
+
const encoder = new TextEncoder();
|
|
2532
|
+
buf.set(encoder.encode(MAGIC_PREFIX), 0);
|
|
2533
|
+
const view = new DataView(buf.buffer);
|
|
2534
|
+
view.setUint32(24, VERSION, false);
|
|
2535
|
+
view.setUint32(28, PAYLOAD_LENGTH, false);
|
|
2536
|
+
return bytesToHex(buf);
|
|
2537
|
+
}
|
|
2538
|
+
function isForwarderHookData(hex) {
|
|
2539
|
+
if (!hex.startsWith("0x")) return false;
|
|
2540
|
+
return hex.toLowerCase().startsWith(
|
|
2541
|
+
"0x636374702d666f72776172640000000000000000000000000000000000000000"
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
var ANY_DESTINATION_CALLER = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
2545
|
+
function bytesToHex(bytes) {
|
|
2546
|
+
let out = "0x";
|
|
2547
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2548
|
+
const byte = bytes[i];
|
|
2549
|
+
if (byte === void 0) continue;
|
|
2550
|
+
out += byte.toString(16).padStart(2, "0");
|
|
2551
|
+
}
|
|
2552
|
+
return out;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
// src/encoding/address.ts
|
|
2556
|
+
var ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
|
|
2557
|
+
var InvalidAddressError = class extends InvalidInputError {
|
|
2558
|
+
constructor(input) {
|
|
2559
|
+
super("invalid_address", `Invalid EVM address: ${input}`);
|
|
2560
|
+
this.name = "InvalidAddressError";
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
var InvalidBytes32Error = class extends InvalidInputError {
|
|
2564
|
+
constructor(input) {
|
|
2565
|
+
super("invalid_bytes32", `Invalid bytes32 value: ${input}`);
|
|
2566
|
+
this.name = "InvalidBytes32Error";
|
|
2567
|
+
}
|
|
2568
|
+
};
|
|
2569
|
+
function toBytes32(address) {
|
|
2570
|
+
if (!ADDRESS_REGEX.test(address)) {
|
|
2571
|
+
throw new InvalidAddressError(address);
|
|
2572
|
+
}
|
|
2573
|
+
const hex = address.slice(2).toLowerCase();
|
|
2574
|
+
return `0x${"00".repeat(12)}${hex}`;
|
|
2575
|
+
}
|
|
2576
|
+
function fromBytes32(value) {
|
|
2577
|
+
const cleaned = value.toLowerCase();
|
|
2578
|
+
if (!/^0x[0-9a-f]{64}$/.test(cleaned)) {
|
|
2579
|
+
throw new InvalidBytes32Error(value);
|
|
2580
|
+
}
|
|
2581
|
+
const upper = cleaned.slice(2, 2 + 24);
|
|
2582
|
+
if (upper !== "00".repeat(12)) {
|
|
2583
|
+
throw new InvalidBytes32Error(
|
|
2584
|
+
`non-zero upper 12 bytes (not an EVM address): ${value}`
|
|
2585
|
+
);
|
|
2586
|
+
}
|
|
2587
|
+
return `0x${cleaned.slice(26)}`;
|
|
2588
|
+
}
|
|
2589
|
+
function tryFromBytes32(value) {
|
|
2590
|
+
try {
|
|
2591
|
+
return fromBytes32(value);
|
|
2592
|
+
} catch {
|
|
2593
|
+
return void 0;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
function isAddress(value) {
|
|
2597
|
+
return ADDRESS_REGEX.test(value);
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
// src/fees/minFee.ts
|
|
2601
|
+
var GET_MIN_FEE_AMOUNT = "0x516990e3";
|
|
2602
|
+
async function getStandardMinFee(chain, amount, options = {}) {
|
|
2603
|
+
if (!chain.capabilities.standardFeeSwitch) {
|
|
2604
|
+
throw new InvalidInputError(
|
|
2605
|
+
"invalid_chain",
|
|
2606
|
+
`${chain.id} has no getMinFeeAmount; its TokenMessengerV2 reverts the call`
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
if (amount < 0n) {
|
|
2610
|
+
throw new InvalidInputError(
|
|
2611
|
+
"invalid_amount",
|
|
2612
|
+
`amount must be non-negative, got ${amount}`
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
return callRpc(
|
|
2616
|
+
chain,
|
|
2617
|
+
"eth_call",
|
|
2618
|
+
[
|
|
2619
|
+
{
|
|
2620
|
+
to: chain.tokenMessenger,
|
|
2621
|
+
data: `${GET_MIN_FEE_AMOUNT}${amount.toString(16).padStart(64, "0")}`
|
|
2622
|
+
},
|
|
2623
|
+
"latest"
|
|
2624
|
+
],
|
|
2625
|
+
(r) => typeof r === "string" && /^0x[0-9a-fA-F]{64}$/.test(r) ? BigInt(r) : void 0,
|
|
2626
|
+
options
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
// src/message/decode.ts
|
|
2631
|
+
var HEADER_BYTES = 148;
|
|
2632
|
+
var BODY_BYTES = 228;
|
|
2633
|
+
function decodeBurnMessage(message) {
|
|
2634
|
+
if (!/^0x([0-9a-fA-F]{2})*$/.test(message)) {
|
|
2635
|
+
throw new InvalidInputError("invalid_input", "message is not hex bytes");
|
|
2636
|
+
}
|
|
2637
|
+
const hex = message.slice(2).toLowerCase();
|
|
2638
|
+
if (hex.length < (HEADER_BYTES + BODY_BYTES) * 2) {
|
|
2639
|
+
throw new InvalidInputError(
|
|
2640
|
+
"invalid_input",
|
|
2641
|
+
`a burn message is at least ${HEADER_BYTES + BODY_BYTES} bytes, got ${hex.length / 2}`
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
let at = 0;
|
|
2645
|
+
const take = (bytes) => {
|
|
2646
|
+
const part = hex.slice(at, at + bytes * 2);
|
|
2647
|
+
at += bytes * 2;
|
|
2648
|
+
return part;
|
|
2649
|
+
};
|
|
2650
|
+
const uint = (bytes) => BigInt(`0x${take(bytes)}`);
|
|
2651
|
+
const word = () => `0x${take(32)}`;
|
|
2652
|
+
const small = (bytes) => Number.parseInt(take(bytes), 16);
|
|
2653
|
+
return {
|
|
2654
|
+
version: small(4),
|
|
2655
|
+
sourceDomain: small(4),
|
|
2656
|
+
destinationDomain: small(4),
|
|
2657
|
+
nonce: word(),
|
|
2658
|
+
sender: word(),
|
|
2659
|
+
recipient: word(),
|
|
2660
|
+
destinationCaller: word(),
|
|
2661
|
+
minFinalityThreshold: small(4),
|
|
2662
|
+
finalityThresholdExecuted: small(4),
|
|
2663
|
+
body: {
|
|
2664
|
+
version: small(4),
|
|
2665
|
+
burnToken: word(),
|
|
2666
|
+
mintRecipient: word(),
|
|
2667
|
+
amount: uint(32),
|
|
2668
|
+
messageSender: word(),
|
|
2669
|
+
maxFee: uint(32),
|
|
2670
|
+
feeExecuted: uint(32),
|
|
2671
|
+
expirationBlock: uint(32),
|
|
2672
|
+
hookData: `0x${hex.slice(at)}`
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
function encodeMintRecipient(destination, mintRecipient) {
|
|
2677
|
+
if (destination.ecosystem === "solana") {
|
|
2678
|
+
try {
|
|
2679
|
+
new PublicKey(mintRecipient);
|
|
2680
|
+
} catch {
|
|
2681
|
+
throw new InvalidInputError(
|
|
2682
|
+
"invalid_address",
|
|
2683
|
+
`mintRecipient is not a valid Solana pubkey: ${mintRecipient}`
|
|
2684
|
+
);
|
|
2685
|
+
}
|
|
2686
|
+
} else if (destination.ecosystem === "stellar") {
|
|
2687
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(mintRecipient)) {
|
|
2688
|
+
throw new InvalidInputError(
|
|
2689
|
+
"invalid_address",
|
|
2690
|
+
`mintRecipient for Stellar must be 0x-prefixed 32-byte hex (the CctpForwarder strkey decoded); got: ${mintRecipient}`
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
} else if (destination.ecosystem === "starknet") {
|
|
2694
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(mintRecipient)) {
|
|
2695
|
+
throw new InvalidInputError(
|
|
2696
|
+
"invalid_address",
|
|
2697
|
+
`mintRecipient for Starknet must be 0x-prefixed 32-byte hex; got: ${mintRecipient}`
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
} else if (!isAddress(mintRecipient)) {
|
|
2701
|
+
throw new InvalidInputError(
|
|
2702
|
+
"invalid_address",
|
|
2703
|
+
`mintRecipient is not a valid EVM address: ${mintRecipient}`
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
if (destination.ecosystem === "solana") {
|
|
2707
|
+
const bytes = new PublicKey(mintRecipient).toBytes();
|
|
2708
|
+
let hex = "0x";
|
|
2709
|
+
for (const b of bytes) hex += b.toString(16).padStart(2, "0");
|
|
2710
|
+
return hex;
|
|
2711
|
+
}
|
|
2712
|
+
if (destination.ecosystem === "stellar" || destination.ecosystem === "starknet") {
|
|
2713
|
+
return mintRecipient;
|
|
2714
|
+
}
|
|
2715
|
+
return toBytes32(mintRecipient);
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
// src/burn/build.ts
|
|
2719
|
+
var MAX_USDC_AMOUNT = 10000000n * 1000000n;
|
|
2720
|
+
function buildBurnIntent(input) {
|
|
2721
|
+
if (input.source.ecosystem !== "evm") {
|
|
2722
|
+
throw new UnsupportedError(
|
|
2723
|
+
"unsupported_ecosystem",
|
|
2724
|
+
`Source chain "${input.source.id}" is not EVM. Use the Solana burn builder for Solana sources.`
|
|
2725
|
+
);
|
|
2726
|
+
}
|
|
2727
|
+
if (input.source.cctpDomain === input.destination.cctpDomain) {
|
|
2728
|
+
throw new InvalidInputError(
|
|
2729
|
+
"no_route",
|
|
2730
|
+
`Source and destination domains are identical (${input.source.cctpDomain}). Use a same-chain transfer instead of bridge.`
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
if (input.amount <= 0n) {
|
|
2734
|
+
throw new InvalidInputError(
|
|
2735
|
+
"invalid_amount",
|
|
2736
|
+
`amount must be positive, got ${input.amount}`
|
|
2737
|
+
);
|
|
2738
|
+
}
|
|
2739
|
+
if (input.amount > MAX_USDC_AMOUNT) {
|
|
2740
|
+
throw new ProtocolError(
|
|
2741
|
+
"amount_too_large",
|
|
2742
|
+
`amount exceeds CCTP per-burn cap of 10,000,000 USDC (got ${input.amount} subunits)`
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
const mintRecipientBytes32 = encodeMintRecipient(
|
|
2746
|
+
input.destination,
|
|
2747
|
+
input.mintRecipient
|
|
2748
|
+
);
|
|
2749
|
+
const finalityThreshold = input.fee.tier.finalityThreshold;
|
|
2750
|
+
if (finalityThreshold === FINALITY_FAST && !input.source.capabilities.fastSource) {
|
|
2751
|
+
throw new ProtocolError(
|
|
2752
|
+
"no_route",
|
|
2753
|
+
`Source chain "${input.source.id}" has no Fast Transfer. Use its Standard fee tier.`
|
|
2754
|
+
);
|
|
2755
|
+
}
|
|
2756
|
+
const withForwarder = input.withForwarder ?? true;
|
|
2757
|
+
const destinationCaller = input.destinationCaller ?? ANY_DESTINATION_CALLER;
|
|
2758
|
+
const computed = computeMaxFee({
|
|
2759
|
+
amount: input.amount,
|
|
2760
|
+
tier: input.fee.tier,
|
|
2761
|
+
...input.fee.forwarderLevel !== void 0 && {
|
|
2762
|
+
forwarderLevel: input.fee.forwarderLevel
|
|
2763
|
+
},
|
|
2764
|
+
...input.fee.forwarderBufferBps !== void 0 && {
|
|
2765
|
+
forwarderBufferBps: input.fee.forwarderBufferBps
|
|
2766
|
+
},
|
|
2767
|
+
includeForwarder: withForwarder
|
|
2768
|
+
});
|
|
2769
|
+
const { burnFee, forwarderFee } = computed;
|
|
2770
|
+
const maxFee = input.fee.maxFee ?? computed.maxFee;
|
|
2771
|
+
if (maxFee < 0n || maxFee >= input.amount) {
|
|
2772
|
+
throw new InvalidInputError(
|
|
2773
|
+
"invalid_amount",
|
|
2774
|
+
`maxFee must be at least 0 and below amount (TokenMessengerV2 reverts otherwise), got ${maxFee}`
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2777
|
+
const calls = [];
|
|
2778
|
+
if (!input.skipApprove) {
|
|
2779
|
+
const approveAmount = input.approveAmount ?? input.amount;
|
|
2780
|
+
if (approveAmount < input.amount) {
|
|
2781
|
+
throw new InvalidInputError(
|
|
2782
|
+
"invalid_amount",
|
|
2783
|
+
`approveAmount (${approveAmount}) must be \u2265 amount (${input.amount})`
|
|
2784
|
+
);
|
|
2785
|
+
}
|
|
2786
|
+
calls.push({
|
|
2787
|
+
to: input.source.usdc,
|
|
2788
|
+
data: encodeFunctionData({
|
|
2789
|
+
abi: usdcAbi,
|
|
2790
|
+
functionName: "approve",
|
|
2791
|
+
args: [input.source.tokenMessenger, approveAmount]
|
|
2792
|
+
}),
|
|
2793
|
+
value: 0n
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
const burnArgs = [
|
|
2797
|
+
input.amount,
|
|
2798
|
+
input.destination.cctpDomain,
|
|
2799
|
+
mintRecipientBytes32,
|
|
2800
|
+
input.source.usdc,
|
|
2801
|
+
destinationCaller,
|
|
2802
|
+
maxFee,
|
|
2803
|
+
finalityThreshold
|
|
2804
|
+
];
|
|
2805
|
+
const effectiveHookData = input.customHookData ?? CCTP_FORWARD_HOOK_DATA;
|
|
2806
|
+
const callWithHook = withForwarder || input.customHookData !== void 0;
|
|
2807
|
+
calls.push({
|
|
2808
|
+
to: input.source.tokenMessenger,
|
|
2809
|
+
data: callWithHook ? encodeFunctionData({
|
|
2810
|
+
abi: tokenMessengerV2Abi,
|
|
2811
|
+
functionName: "depositForBurnWithHook",
|
|
2812
|
+
args: [...burnArgs, effectiveHookData]
|
|
2813
|
+
}) : encodeFunctionData({
|
|
2814
|
+
abi: tokenMessengerV2Abi,
|
|
2815
|
+
functionName: "depositForBurn",
|
|
2816
|
+
args: burnArgs
|
|
2817
|
+
}),
|
|
2818
|
+
value: 0n
|
|
2819
|
+
});
|
|
2820
|
+
return {
|
|
2821
|
+
source: input.source,
|
|
2822
|
+
destination: input.destination,
|
|
2823
|
+
calls,
|
|
2824
|
+
amount: input.amount,
|
|
2825
|
+
maxFee,
|
|
2826
|
+
burnFee,
|
|
2827
|
+
forwarderFee,
|
|
2828
|
+
mintRecipientBytes32,
|
|
2829
|
+
withForwarder,
|
|
2830
|
+
finalityThreshold
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
function buildBurnWithFeesIntent(input) {
|
|
2834
|
+
const { source, destination } = input;
|
|
2835
|
+
if (source.ecosystem !== "evm" || !source.tokenMessengerWithFees) {
|
|
2836
|
+
throw new UnsupportedError(
|
|
2837
|
+
"unsupported_chain",
|
|
2838
|
+
`${source.id} has no TokenMessengerWithFees, so it cannot pay fees upfront`
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
if (destination.ecosystem === "stellar") {
|
|
2842
|
+
throw new UnsupportedError(
|
|
2843
|
+
"unsupported_chain",
|
|
2844
|
+
"burns to Stellar carry custom hook data and cannot pay fees upfront"
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
if (source.cctpDomain === destination.cctpDomain) {
|
|
2848
|
+
throw new InvalidInputError(
|
|
2849
|
+
"no_route",
|
|
2850
|
+
`Source and destination domains are identical (${source.cctpDomain})`
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
if (input.amount <= 0n || input.fee < 0n) {
|
|
2854
|
+
throw new InvalidInputError(
|
|
2855
|
+
"invalid_amount",
|
|
2856
|
+
`amount must be positive and fee non-negative, got ${input.amount} and ${input.fee}`
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
if (!isAddress(input.refundAddress)) {
|
|
2860
|
+
throw new InvalidInputError(
|
|
2861
|
+
"invalid_address",
|
|
2862
|
+
`refundAddress is not a valid EVM address: ${input.refundAddress}`
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
const mintRecipientBytes32 = encodeMintRecipient(
|
|
2866
|
+
destination,
|
|
2867
|
+
input.mintRecipient
|
|
2868
|
+
);
|
|
2869
|
+
const approveAmount = input.approveAmount ?? input.amount + input.fee;
|
|
2870
|
+
if (approveAmount < input.amount + input.fee) {
|
|
2871
|
+
throw new InvalidInputError(
|
|
2872
|
+
"invalid_amount",
|
|
2873
|
+
`approveAmount (${approveAmount}) must cover amount plus fee (${input.amount + input.fee})`
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
const calls = [
|
|
2877
|
+
{
|
|
2878
|
+
to: source.usdc,
|
|
2879
|
+
data: encodeFunctionData({
|
|
2880
|
+
abi: usdcAbi,
|
|
2881
|
+
functionName: "approve",
|
|
2882
|
+
args: [source.tokenMessengerWithFees, approveAmount]
|
|
2883
|
+
}),
|
|
2884
|
+
value: 0n
|
|
2885
|
+
},
|
|
2886
|
+
{
|
|
2887
|
+
to: source.tokenMessengerWithFees,
|
|
2888
|
+
data: encodeFunctionData({
|
|
2889
|
+
abi: tokenMessengerWithFeesAbi,
|
|
2890
|
+
functionName: "depositForBurnWithFees",
|
|
2891
|
+
args: [
|
|
2892
|
+
input.amount,
|
|
2893
|
+
destination.cctpDomain,
|
|
2894
|
+
mintRecipientBytes32,
|
|
2895
|
+
source.usdc,
|
|
2896
|
+
ANY_DESTINATION_CALLER,
|
|
2897
|
+
{
|
|
2898
|
+
signedQuote: input.signedQuote,
|
|
2899
|
+
refundAddress: input.refundAddress
|
|
2900
|
+
}
|
|
2901
|
+
]
|
|
2902
|
+
}),
|
|
2903
|
+
value: 0n
|
|
2904
|
+
}
|
|
2905
|
+
];
|
|
2906
|
+
return { calls, amount: input.amount, fee: input.fee, mintRecipientBytes32 };
|
|
2907
|
+
}
|
|
2908
|
+
var HEX_PREFIX_REGEX = /^0x[0-9a-fA-F]+$/;
|
|
2909
|
+
var NON_EVM_BUILDERS = {
|
|
2910
|
+
solana: "buildSolanaReceiveIntent",
|
|
2911
|
+
stellar: "buildStellarMintIntent",
|
|
2912
|
+
starknet: "buildStarknetReceiveIntent"
|
|
2913
|
+
};
|
|
2914
|
+
function buildMintIntent(input) {
|
|
2915
|
+
if (input.destination.ecosystem !== "evm") {
|
|
2916
|
+
throw new UnsupportedError(
|
|
2917
|
+
"unsupported_ecosystem",
|
|
2918
|
+
`buildMintIntent builds EVM mints. Use ${NON_EVM_BUILDERS[input.destination.ecosystem]} for "${input.destination.id}".`
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2921
|
+
if (!HEX_PREFIX_REGEX.test(input.message)) {
|
|
2922
|
+
throw new InvalidInputError(
|
|
2923
|
+
"invalid_input",
|
|
2924
|
+
"message must be a 0x-prefixed hex string"
|
|
2925
|
+
);
|
|
2926
|
+
}
|
|
2927
|
+
if (!HEX_PREFIX_REGEX.test(input.attestation)) {
|
|
2928
|
+
throw new InvalidInputError(
|
|
2929
|
+
"invalid_input",
|
|
2930
|
+
"attestation must be a 0x-prefixed hex string"
|
|
2931
|
+
);
|
|
2932
|
+
}
|
|
2933
|
+
const data = encodeFunctionData({
|
|
2934
|
+
abi: messageTransmitterV2Abi,
|
|
2935
|
+
functionName: "receiveMessage",
|
|
2936
|
+
args: [input.message, input.attestation]
|
|
2937
|
+
});
|
|
2938
|
+
return {
|
|
2939
|
+
destination: input.destination,
|
|
2940
|
+
call: {
|
|
2941
|
+
to: input.destination.messageTransmitter,
|
|
2942
|
+
data,
|
|
2943
|
+
value: 0n
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
// src/starknet/address.ts
|
|
2949
|
+
var STARKNET_FELT_RE = /^0x[a-fA-F0-9]{1,64}$/;
|
|
2950
|
+
function isValidStarknetFelt(s2) {
|
|
2951
|
+
return STARKNET_FELT_RE.test(s2);
|
|
2952
|
+
}
|
|
2953
|
+
function assertStarknetFelt(s2) {
|
|
2954
|
+
if (!isValidStarknetFelt(s2)) {
|
|
2955
|
+
throw new InvalidInputError(
|
|
2956
|
+
"invalid_address",
|
|
2957
|
+
`Not a valid Starknet felt: ${s2}`
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
return s2;
|
|
2961
|
+
}
|
|
2962
|
+
function feltToBytes32Hex(felt) {
|
|
2963
|
+
if (!isValidStarknetFelt(felt)) {
|
|
2964
|
+
throw new InvalidInputError(
|
|
2965
|
+
"invalid_address",
|
|
2966
|
+
`Not a valid Starknet felt: ${felt}`
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
const stripped = felt.slice(2);
|
|
2970
|
+
if (stripped.length > 64) {
|
|
2971
|
+
throw new InvalidInputError(
|
|
2972
|
+
"invalid_address",
|
|
2973
|
+
`Starknet felt exceeds 32 bytes: ${felt}`
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
return `0x${stripped.padStart(64, "0")}`;
|
|
2977
|
+
}
|
|
2978
|
+
function bytes32HexToFelt(hex) {
|
|
2979
|
+
const stripped = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
2980
|
+
if (stripped.length > 64) {
|
|
2981
|
+
throw new InvalidInputError(
|
|
2982
|
+
"invalid_address",
|
|
2983
|
+
`bytes32 input exceeds 32 bytes (${stripped.length / 2})`
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
const stripped64 = stripped.padStart(64, "0").replace(/^0+/, "");
|
|
2987
|
+
const canonical = stripped64.length === 0 ? "0" : stripped64;
|
|
2988
|
+
return `0x${canonical}`;
|
|
2989
|
+
}
|
|
2990
|
+
function feltToBigInt(felt) {
|
|
2991
|
+
if (!isValidStarknetFelt(felt)) {
|
|
2992
|
+
throw new InvalidInputError(
|
|
2993
|
+
"invalid_address",
|
|
2994
|
+
`Not a valid Starknet felt: ${felt}`
|
|
2995
|
+
);
|
|
2996
|
+
}
|
|
2997
|
+
return BigInt(felt);
|
|
2998
|
+
}
|
|
2999
|
+
function u256ToFelts(value) {
|
|
3000
|
+
if (value < 0n) {
|
|
3001
|
+
throw new RangeError(
|
|
3002
|
+
`u256ToFelts: value must be non-negative, got ${value}`
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
3005
|
+
const u = cairo.uint256(value);
|
|
3006
|
+
return [u.low.toString(), u.high.toString()];
|
|
3007
|
+
}
|
|
3008
|
+
function feltToString(value) {
|
|
3009
|
+
return num.toBigInt(value).toString();
|
|
3010
|
+
}
|
|
3011
|
+
function bytesToByteArrayFelts(bytes) {
|
|
3012
|
+
const CHUNK = 31;
|
|
3013
|
+
const dataFelts = [];
|
|
3014
|
+
let i = 0;
|
|
3015
|
+
while (i + CHUNK <= bytes.length) {
|
|
3016
|
+
dataFelts.push(bytesToBigIntBE(bytes.subarray(i, i + CHUNK)).toString());
|
|
3017
|
+
i += CHUNK;
|
|
3018
|
+
}
|
|
3019
|
+
const rest = bytes.subarray(i);
|
|
3020
|
+
const pendingWord = bytesToBigIntBE(rest).toString();
|
|
3021
|
+
const pendingWordLen = rest.length.toString();
|
|
3022
|
+
return [
|
|
3023
|
+
dataFelts.length.toString(),
|
|
3024
|
+
...dataFelts,
|
|
3025
|
+
pendingWord,
|
|
3026
|
+
pendingWordLen
|
|
3027
|
+
];
|
|
3028
|
+
}
|
|
3029
|
+
function bytesToBigIntBE(bytes) {
|
|
3030
|
+
let n = 0n;
|
|
3031
|
+
for (const b of bytes) n = n << 8n | BigInt(b);
|
|
3032
|
+
return n;
|
|
3033
|
+
}
|
|
3034
|
+
function hexToByteArrayFelts(hex) {
|
|
3035
|
+
const stripped = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
3036
|
+
const bytes = new Uint8Array(stripped.length / 2);
|
|
3037
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
3038
|
+
bytes[i] = parseInt(stripped.slice(i * 2, i * 2 + 2), 16);
|
|
3039
|
+
}
|
|
3040
|
+
return bytesToByteArrayFelts(bytes);
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
// src/starknet/burn.ts
|
|
3044
|
+
function buildStarknetBurnIntent(input) {
|
|
3045
|
+
if (input.source.ecosystem !== "starknet") {
|
|
3046
|
+
throw new InvalidInputError(
|
|
3047
|
+
"invalid_chain",
|
|
3048
|
+
`source must be Starknet, got ${input.source.ecosystem}`
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
if (input.amount <= 0n) {
|
|
3052
|
+
throw new InvalidInputError(
|
|
3053
|
+
"invalid_amount",
|
|
3054
|
+
`amount must be positive, got ${input.amount}`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
if (input.maxFee < 0n) {
|
|
3058
|
+
throw new InvalidInputError(
|
|
3059
|
+
"invalid_amount",
|
|
3060
|
+
`maxFee must be non-negative, got ${input.maxFee}`
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
if (input.mintRecipient < 0n) {
|
|
3064
|
+
throw new InvalidInputError(
|
|
3065
|
+
"invalid_input",
|
|
3066
|
+
`mintRecipient must be non-negative, got ${input.mintRecipient}`
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
const withHook = !!input.hookData && input.hookData.length > 0;
|
|
3070
|
+
const entrypoint = withHook ? "deposit_for_burn_with_hook" : "deposit_for_burn";
|
|
3071
|
+
const [amountLo, amountHi] = u256ToFelts(input.amount);
|
|
3072
|
+
const [recipLo, recipHi] = u256ToFelts(input.mintRecipient);
|
|
3073
|
+
const [callerLo, callerHi] = u256ToFelts(input.destinationCaller);
|
|
3074
|
+
const [maxFeeLo, maxFeeHi] = u256ToFelts(input.maxFee);
|
|
3075
|
+
const calldata = [
|
|
3076
|
+
amountLo,
|
|
3077
|
+
amountHi,
|
|
3078
|
+
feltToString(input.destinationDomain),
|
|
3079
|
+
recipLo,
|
|
3080
|
+
recipHi,
|
|
3081
|
+
assertStarknetFelt(input.source.usdc),
|
|
3082
|
+
callerLo,
|
|
3083
|
+
callerHi,
|
|
3084
|
+
maxFeeLo,
|
|
3085
|
+
maxFeeHi,
|
|
3086
|
+
feltToString(input.minFinalityThreshold)
|
|
3087
|
+
];
|
|
3088
|
+
if (withHook) {
|
|
3089
|
+
calldata.push(...bytesToByteArrayFelts(input.hookData));
|
|
3090
|
+
}
|
|
3091
|
+
return {
|
|
3092
|
+
source: input.source,
|
|
3093
|
+
destinationDomain: input.destinationDomain,
|
|
3094
|
+
amount: input.amount,
|
|
3095
|
+
maxFee: input.maxFee,
|
|
3096
|
+
call: {
|
|
3097
|
+
contractAddress: input.source.tokenMessengerMinter,
|
|
3098
|
+
entrypoint,
|
|
3099
|
+
calldata
|
|
3100
|
+
},
|
|
3101
|
+
withHook
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
function buildStarknetApproveCall(args) {
|
|
3105
|
+
if (args.source.ecosystem !== "starknet") {
|
|
3106
|
+
throw new InvalidInputError(
|
|
3107
|
+
"invalid_chain",
|
|
3108
|
+
`source must be Starknet, got ${args.source.ecosystem}`
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
if (args.amount <= 0n) {
|
|
3112
|
+
throw new InvalidInputError(
|
|
3113
|
+
"invalid_amount",
|
|
3114
|
+
`amount must be positive, got ${args.amount}`
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
const [lo, hi] = u256ToFelts(args.amount);
|
|
3118
|
+
return {
|
|
3119
|
+
contractAddress: args.source.usdc,
|
|
3120
|
+
entrypoint: "approve",
|
|
3121
|
+
calldata: [assertStarknetFelt(args.spender), lo, hi]
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
// src/starknet/receive.ts
|
|
3126
|
+
function buildStarknetReceiveIntent(input) {
|
|
3127
|
+
if (input.destination.ecosystem !== "starknet") {
|
|
3128
|
+
throw new InvalidInputError(
|
|
3129
|
+
"invalid_chain",
|
|
3130
|
+
`destination must be Starknet, got ${input.destination.ecosystem}`
|
|
3131
|
+
);
|
|
3132
|
+
}
|
|
3133
|
+
if (!input.message.startsWith("0x") || input.message.length < 90) {
|
|
3134
|
+
throw new InvalidInputError(
|
|
3135
|
+
"invalid_input",
|
|
3136
|
+
"message must be a 0x-prefixed hex blob of at least 44 bytes"
|
|
3137
|
+
);
|
|
3138
|
+
}
|
|
3139
|
+
if (!input.attestation.startsWith("0x") || input.attestation.length < 4) {
|
|
3140
|
+
throw new InvalidInputError(
|
|
3141
|
+
"invalid_input",
|
|
3142
|
+
"attestation must be a 0x-prefixed hex blob"
|
|
3143
|
+
);
|
|
3144
|
+
}
|
|
3145
|
+
const calldata = [
|
|
3146
|
+
...hexToByteArrayFelts(input.message),
|
|
3147
|
+
...hexToByteArrayFelts(input.attestation)
|
|
3148
|
+
];
|
|
3149
|
+
return {
|
|
3150
|
+
destination: input.destination,
|
|
3151
|
+
call: {
|
|
3152
|
+
contractAddress: input.destination.messageTransmitter,
|
|
3153
|
+
entrypoint: "receive_message",
|
|
3154
|
+
calldata
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
function isValidStellarStrkey(s2) {
|
|
3159
|
+
return StrKey.isValidEd25519PublicKey(s2) || StrKey.isValidMed25519PublicKey(s2) || StrKey.isValidContract(s2);
|
|
3160
|
+
}
|
|
3161
|
+
function strkeyType(s2) {
|
|
3162
|
+
if (StrKey.isValidEd25519PublicKey(s2)) return "G";
|
|
3163
|
+
if (StrKey.isValidMed25519PublicKey(s2)) return "M";
|
|
3164
|
+
if (StrKey.isValidContract(s2)) return "C";
|
|
3165
|
+
throw new InvalidInputError(
|
|
3166
|
+
"invalid_address",
|
|
3167
|
+
`Invalid Stellar strkey: ${s2}`
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
function decodeStrkeyToBytes32(s2) {
|
|
3171
|
+
if (StrKey.isValidEd25519PublicKey(s2))
|
|
3172
|
+
return StrKey.decodeEd25519PublicKey(s2);
|
|
3173
|
+
if (StrKey.isValidContract(s2)) return StrKey.decodeContract(s2);
|
|
3174
|
+
if (StrKey.isValidMed25519PublicKey(s2)) {
|
|
3175
|
+
return StrKey.decodeMed25519PublicKey(s2).slice(0, 32);
|
|
3176
|
+
}
|
|
3177
|
+
throw new InvalidInputError(
|
|
3178
|
+
"invalid_address",
|
|
3179
|
+
`Invalid Stellar strkey: ${s2}`
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
function encodeContractStrkey(bytes) {
|
|
3183
|
+
if (bytes.length !== 32) {
|
|
3184
|
+
throw new InvalidInputError(
|
|
3185
|
+
"invalid_input",
|
|
3186
|
+
`encodeContractStrkey: bytes must be 32, got ${bytes.length}`
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
return StrKey.encodeContract(Buffer.from(bytes));
|
|
3190
|
+
}
|
|
3191
|
+
function contractStrkeyToBytes32Hex(s2) {
|
|
3192
|
+
if (!StrKey.isValidContract(s2)) {
|
|
3193
|
+
throw new InvalidInputError(
|
|
3194
|
+
"invalid_address",
|
|
3195
|
+
`Not a Stellar contract strkey (C\u2026): ${s2}`
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
const bytes = StrKey.decodeContract(s2);
|
|
3199
|
+
let hex = "0x";
|
|
3200
|
+
for (const b of bytes) hex += b.toString(16).padStart(2, "0");
|
|
3201
|
+
return hex;
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
// src/stellar/precision.ts
|
|
3205
|
+
var DECIMAL_FACTOR = 10n ** BigInt(STELLAR_USDC_DECIMALS - CCTP_USDC_DECIMALS);
|
|
3206
|
+
function stellarSubunitsToCanonical(stellarSubunits) {
|
|
3207
|
+
if (stellarSubunits < 0n) {
|
|
3208
|
+
throw new RangeError(
|
|
3209
|
+
`stellarSubunitsToCanonical: amount must be non-negative, got ${stellarSubunits}`
|
|
3210
|
+
);
|
|
3211
|
+
}
|
|
3212
|
+
return stellarSubunits / DECIMAL_FACTOR;
|
|
3213
|
+
}
|
|
3214
|
+
function canonicalToStellarSubunits(canonicalSubunits) {
|
|
3215
|
+
if (canonicalSubunits < 0n) {
|
|
3216
|
+
throw new RangeError(
|
|
3217
|
+
`canonicalToStellarSubunits: amount must be non-negative, got ${canonicalSubunits}`
|
|
3218
|
+
);
|
|
3219
|
+
}
|
|
3220
|
+
return canonicalSubunits * DECIMAL_FACTOR;
|
|
3221
|
+
}
|
|
3222
|
+
function stellarBurnDust(stellarSubunits) {
|
|
3223
|
+
return stellarSubunits % DECIMAL_FACTOR;
|
|
3224
|
+
}
|
|
3225
|
+
var VERSION_OFFSET = 24;
|
|
3226
|
+
var LENGTH_OFFSET = 28;
|
|
3227
|
+
var PAYLOAD_OFFSET = 32;
|
|
3228
|
+
var HOOK_DATA_VERSION = 0;
|
|
3229
|
+
function buildStellarForwarderHookData(forwardRecipientStrkey, integratorPayload) {
|
|
3230
|
+
if (!isValidStellarStrkey(forwardRecipientStrkey)) {
|
|
3231
|
+
throw new InvalidInputError(
|
|
3232
|
+
"invalid_address",
|
|
3233
|
+
`Stellar forwardRecipient must be a G/M/C strkey, got: ${forwardRecipientStrkey}`
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
const recipientBytes = new TextEncoder().encode(forwardRecipientStrkey);
|
|
3237
|
+
const payloadLength = integratorPayload?.length ?? 0;
|
|
3238
|
+
const total = PAYLOAD_OFFSET + recipientBytes.length + payloadLength;
|
|
3239
|
+
const buf = new Uint8Array(total);
|
|
3240
|
+
writeU32BE(buf, VERSION_OFFSET, HOOK_DATA_VERSION);
|
|
3241
|
+
writeU32BE(buf, LENGTH_OFFSET, recipientBytes.length);
|
|
3242
|
+
buf.set(recipientBytes, PAYLOAD_OFFSET);
|
|
3243
|
+
if (integratorPayload && integratorPayload.length > 0) {
|
|
3244
|
+
buf.set(integratorPayload, PAYLOAD_OFFSET + recipientBytes.length);
|
|
3245
|
+
}
|
|
3246
|
+
let hex = "0x";
|
|
3247
|
+
for (const b of buf) hex += b.toString(16).padStart(2, "0");
|
|
3248
|
+
return hex;
|
|
3249
|
+
}
|
|
3250
|
+
function writeU32BE(buf, offset, value) {
|
|
3251
|
+
buf[offset] = value >>> 24 & 255;
|
|
3252
|
+
buf[offset + 1] = value >>> 16 & 255;
|
|
3253
|
+
buf[offset + 2] = value >>> 8 & 255;
|
|
3254
|
+
buf[offset + 3] = value & 255;
|
|
3255
|
+
}
|
|
3256
|
+
function buildStellarApproveInvocation(input) {
|
|
3257
|
+
if (input.source.ecosystem !== "stellar") {
|
|
3258
|
+
throw new InvalidInputError(
|
|
3259
|
+
"invalid_chain",
|
|
3260
|
+
`source must be Stellar, got ${input.source.ecosystem}`
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3263
|
+
if (input.amount <= 0n) {
|
|
3264
|
+
throw new InvalidInputError(
|
|
3265
|
+
"invalid_amount",
|
|
3266
|
+
`amount must be positive, got ${input.amount}`
|
|
3267
|
+
);
|
|
3268
|
+
}
|
|
3269
|
+
if (input.expirationLedger < 0) {
|
|
3270
|
+
throw new InvalidInputError(
|
|
3271
|
+
"invalid_input",
|
|
3272
|
+
`expirationLedger must be \u2265 0, got ${input.expirationLedger}`
|
|
3273
|
+
);
|
|
3274
|
+
}
|
|
3275
|
+
const args = [
|
|
3276
|
+
new Address(input.owner).toScVal(),
|
|
3277
|
+
new Address(input.spender).toScVal(),
|
|
3278
|
+
nativeToScVal(input.amount, { type: "i128" }),
|
|
3279
|
+
nativeToScVal(input.expirationLedger, { type: "u32" })
|
|
3280
|
+
];
|
|
3281
|
+
const operation = new Contract(input.source.usdc).call("approve", ...args);
|
|
3282
|
+
return {
|
|
3283
|
+
ecosystem: "stellar",
|
|
3284
|
+
contractId: input.source.usdc,
|
|
3285
|
+
functionName: "approve",
|
|
3286
|
+
args,
|
|
3287
|
+
operation
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
function buildStellarBurnIntent(input) {
|
|
3291
|
+
if (input.source.ecosystem !== "stellar") {
|
|
3292
|
+
throw new InvalidInputError(
|
|
3293
|
+
"invalid_chain",
|
|
3294
|
+
`source must be a Stellar chain, got ${input.source.ecosystem}`
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
if (!isValidStellarStrkey(input.caller)) {
|
|
3298
|
+
throw new InvalidInputError(
|
|
3299
|
+
"invalid_address",
|
|
3300
|
+
`caller is not a valid Stellar strkey: ${input.caller}`
|
|
3301
|
+
);
|
|
3302
|
+
}
|
|
3303
|
+
if (input.mintRecipientBytes32.length !== 32) {
|
|
3304
|
+
throw new InvalidInputError(
|
|
3305
|
+
"invalid_input",
|
|
3306
|
+
`mintRecipientBytes32 must be 32 bytes, got ${input.mintRecipientBytes32.length}`
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
if (input.destinationCallerBytes32.length !== 32) {
|
|
3310
|
+
throw new InvalidInputError(
|
|
3311
|
+
"invalid_input",
|
|
3312
|
+
`destinationCallerBytes32 must be 32 bytes, got ${input.destinationCallerBytes32.length}`
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3315
|
+
if (input.amount <= 0n) {
|
|
3316
|
+
throw new InvalidInputError(
|
|
3317
|
+
"invalid_amount",
|
|
3318
|
+
`amount must be positive, got ${input.amount}`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
if (input.maxFee < 0n) {
|
|
3322
|
+
throw new InvalidInputError(
|
|
3323
|
+
"invalid_amount",
|
|
3324
|
+
`maxFee must be non-negative, got ${input.maxFee}`
|
|
3325
|
+
);
|
|
3326
|
+
}
|
|
3327
|
+
const withHook = !!input.hookData && input.hookData.length > 0;
|
|
3328
|
+
const fn = withHook ? "deposit_for_burn_with_hook" : "deposit_for_burn";
|
|
3329
|
+
const tmm = input.source.tokenMessengerMinter;
|
|
3330
|
+
const usdcSac = input.source.usdc;
|
|
3331
|
+
const args = [
|
|
3332
|
+
new Address(input.caller).toScVal(),
|
|
3333
|
+
nativeToScVal(input.amount, { type: "i128" }),
|
|
3334
|
+
nativeToScVal(input.destinationDomain, { type: "u32" }),
|
|
3335
|
+
nativeToScVal(Buffer.from(input.mintRecipientBytes32), {
|
|
3336
|
+
type: "bytes"
|
|
3337
|
+
}),
|
|
3338
|
+
new Address(usdcSac).toScVal(),
|
|
3339
|
+
nativeToScVal(Buffer.from(input.destinationCallerBytes32), {
|
|
3340
|
+
type: "bytes"
|
|
3341
|
+
}),
|
|
3342
|
+
nativeToScVal(input.maxFee, { type: "i128" }),
|
|
3343
|
+
nativeToScVal(input.minFinalityThreshold, { type: "u32" })
|
|
3344
|
+
];
|
|
3345
|
+
if (withHook) {
|
|
3346
|
+
args.push(nativeToScVal(Buffer.from(input.hookData), { type: "bytes" }));
|
|
3347
|
+
}
|
|
3348
|
+
const operation = new Contract(tmm).call(fn, ...args);
|
|
3349
|
+
return {
|
|
3350
|
+
source: input.source,
|
|
3351
|
+
destinationDomain: input.destinationDomain,
|
|
3352
|
+
amount: input.amount,
|
|
3353
|
+
maxFee: input.maxFee,
|
|
3354
|
+
invocation: {
|
|
3355
|
+
ecosystem: "stellar",
|
|
3356
|
+
contractId: tmm,
|
|
3357
|
+
functionName: fn,
|
|
3358
|
+
args,
|
|
3359
|
+
operation
|
|
3360
|
+
},
|
|
3361
|
+
withHook
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
function strkeyTo32Bytes(strkey) {
|
|
3365
|
+
return decodeStrkeyToBytes32(strkey);
|
|
3366
|
+
}
|
|
3367
|
+
function buildStellarMintIntent(input) {
|
|
3368
|
+
if (input.destination.ecosystem !== "stellar") {
|
|
3369
|
+
throw new InvalidInputError(
|
|
3370
|
+
"invalid_chain",
|
|
3371
|
+
`destination must be a Stellar chain, got ${input.destination.ecosystem}`
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
if (input.message.length < 44) {
|
|
3375
|
+
throw new InvalidInputError(
|
|
3376
|
+
"invalid_input",
|
|
3377
|
+
`message must be at least 44 bytes, got ${input.message.length}`
|
|
3378
|
+
);
|
|
3379
|
+
}
|
|
3380
|
+
if (input.attestation.length === 0) {
|
|
3381
|
+
throw new InvalidInputError(
|
|
3382
|
+
"invalid_input",
|
|
3383
|
+
"attestation must not be empty"
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
3386
|
+
const args = [
|
|
3387
|
+
nativeToScVal(Buffer.from(input.message), { type: "bytes" }),
|
|
3388
|
+
nativeToScVal(Buffer.from(input.attestation), { type: "bytes" })
|
|
3389
|
+
];
|
|
3390
|
+
const operation = new Contract(input.destination.cctpForwarder).call(
|
|
3391
|
+
"mint_and_forward",
|
|
3392
|
+
...args
|
|
3393
|
+
);
|
|
3394
|
+
return {
|
|
3395
|
+
destination: input.destination,
|
|
3396
|
+
invocation: {
|
|
3397
|
+
ecosystem: "stellar",
|
|
3398
|
+
contractId: input.destination.cctpForwarder,
|
|
3399
|
+
functionName: "mint_and_forward",
|
|
3400
|
+
args,
|
|
3401
|
+
operation
|
|
3402
|
+
}
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
var enc = new TextEncoder();
|
|
3406
|
+
var SEEDS = {
|
|
3407
|
+
tokenMessenger: enc.encode("token_messenger"),
|
|
3408
|
+
tokenMinter: enc.encode("token_minter"),
|
|
3409
|
+
senderAuthority: enc.encode("sender_authority"),
|
|
3410
|
+
remoteTokenMessenger: enc.encode("remote_token_messenger"),
|
|
3411
|
+
localToken: enc.encode("local_token"),
|
|
3412
|
+
tokenPair: enc.encode("token_pair"),
|
|
3413
|
+
custody: enc.encode("custody"),
|
|
3414
|
+
denylistAccount: enc.encode("denylist_account"),
|
|
3415
|
+
messageTransmitter: enc.encode("message_transmitter"),
|
|
3416
|
+
messageTransmitterAuthority: enc.encode("message_transmitter_authority"),
|
|
3417
|
+
usedNonce: enc.encode("used_nonce"),
|
|
3418
|
+
eventAuthority: enc.encode("__event_authority")
|
|
3419
|
+
};
|
|
3420
|
+
function deriveTokenMessenger(tokenMessengerMinterProgram) {
|
|
3421
|
+
return PublicKey.findProgramAddressSync(
|
|
3422
|
+
[SEEDS.tokenMessenger],
|
|
3423
|
+
tokenMessengerMinterProgram
|
|
3424
|
+
)[0];
|
|
3425
|
+
}
|
|
3426
|
+
function deriveTokenMinter(tokenMessengerMinterProgram) {
|
|
3427
|
+
return PublicKey.findProgramAddressSync(
|
|
3428
|
+
[SEEDS.tokenMinter],
|
|
3429
|
+
tokenMessengerMinterProgram
|
|
3430
|
+
)[0];
|
|
3431
|
+
}
|
|
3432
|
+
function deriveSenderAuthority(tokenMessengerMinterProgram) {
|
|
3433
|
+
return PublicKey.findProgramAddressSync(
|
|
3434
|
+
[SEEDS.senderAuthority],
|
|
3435
|
+
tokenMessengerMinterProgram
|
|
3436
|
+
)[0];
|
|
3437
|
+
}
|
|
3438
|
+
function deriveRemoteTokenMessenger(tokenMessengerMinterProgram, domain) {
|
|
3439
|
+
return PublicKey.findProgramAddressSync(
|
|
3440
|
+
[SEEDS.remoteTokenMessenger, enc.encode(domain.toString())],
|
|
3441
|
+
tokenMessengerMinterProgram
|
|
3442
|
+
)[0];
|
|
3443
|
+
}
|
|
3444
|
+
function deriveLocalToken(tokenMessengerMinterProgram, mint) {
|
|
3445
|
+
return PublicKey.findProgramAddressSync(
|
|
3446
|
+
[SEEDS.localToken, mint.toBytes()],
|
|
3447
|
+
tokenMessengerMinterProgram
|
|
3448
|
+
)[0];
|
|
3449
|
+
}
|
|
3450
|
+
function deriveTokenPair(tokenMessengerMinterProgram, sourceDomain, sourceTokenBytes32) {
|
|
3451
|
+
if (sourceTokenBytes32.length !== 32) {
|
|
3452
|
+
throw new Error(
|
|
3453
|
+
`deriveTokenPair: sourceTokenBytes32 must be 32 bytes, got ${sourceTokenBytes32.length}`
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
return PublicKey.findProgramAddressSync(
|
|
3457
|
+
[SEEDS.tokenPair, enc.encode(sourceDomain.toString()), sourceTokenBytes32],
|
|
3458
|
+
tokenMessengerMinterProgram
|
|
3459
|
+
)[0];
|
|
3460
|
+
}
|
|
3461
|
+
function deriveCustodyTokenAccount(tokenMessengerMinterProgram, mint) {
|
|
3462
|
+
return PublicKey.findProgramAddressSync(
|
|
3463
|
+
[SEEDS.custody, mint.toBytes()],
|
|
3464
|
+
tokenMessengerMinterProgram
|
|
3465
|
+
)[0];
|
|
3466
|
+
}
|
|
3467
|
+
function deriveDenylistAccount(tokenMessengerMinterProgram, owner) {
|
|
3468
|
+
return PublicKey.findProgramAddressSync(
|
|
3469
|
+
[SEEDS.denylistAccount, owner.toBytes()],
|
|
3470
|
+
tokenMessengerMinterProgram
|
|
3471
|
+
)[0];
|
|
3472
|
+
}
|
|
3473
|
+
function deriveMessageTransmitter(messageTransmitterProgram) {
|
|
3474
|
+
return PublicKey.findProgramAddressSync(
|
|
3475
|
+
[SEEDS.messageTransmitter],
|
|
3476
|
+
messageTransmitterProgram
|
|
3477
|
+
)[0];
|
|
3478
|
+
}
|
|
3479
|
+
function deriveMessageTransmitterAuthority(messageTransmitterProgram, receiverProgram) {
|
|
3480
|
+
return PublicKey.findProgramAddressSync(
|
|
3481
|
+
[SEEDS.messageTransmitterAuthority, receiverProgram.toBytes()],
|
|
3482
|
+
messageTransmitterProgram
|
|
3483
|
+
)[0];
|
|
3484
|
+
}
|
|
3485
|
+
function deriveUsedNonce(messageTransmitterProgram, messageBytes) {
|
|
3486
|
+
if (messageBytes.length < 44) {
|
|
3487
|
+
throw new Error(
|
|
3488
|
+
`deriveUsedNonce: message must be at least 44 bytes, got ${messageBytes.length}`
|
|
3489
|
+
);
|
|
3490
|
+
}
|
|
3491
|
+
const nonce = messageBytes.slice(12, 44);
|
|
3492
|
+
return PublicKey.findProgramAddressSync(
|
|
3493
|
+
[SEEDS.usedNonce, nonce],
|
|
3494
|
+
messageTransmitterProgram
|
|
3495
|
+
)[0];
|
|
3496
|
+
}
|
|
3497
|
+
function deriveEventAuthority(programId) {
|
|
3498
|
+
return PublicKey.findProgramAddressSync([SEEDS.eventAuthority], programId)[0];
|
|
3499
|
+
}
|
|
3500
|
+
function anchorDiscriminator(snakeCaseName) {
|
|
3501
|
+
const bytes = new TextEncoder().encode(`global:${snakeCaseName}`);
|
|
3502
|
+
return sha256(bytes).slice(0, 8);
|
|
3503
|
+
}
|
|
3504
|
+
var DISCRIMINATOR_DEPOSIT_FOR_BURN = anchorDiscriminator("deposit_for_burn");
|
|
3505
|
+
var DISCRIMINATOR_DEPOSIT_FOR_BURN_WITH_HOOK = anchorDiscriminator("deposit_for_burn_with_hook");
|
|
3506
|
+
var DISCRIMINATOR_RECEIVE_MESSAGE = anchorDiscriminator("receive_message");
|
|
3507
|
+
function createWriter(initialSize = 64) {
|
|
3508
|
+
return { buf: Buffer$1.alloc(initialSize), offset: 0 };
|
|
3509
|
+
}
|
|
3510
|
+
function ensure(w, extra) {
|
|
3511
|
+
if (w.offset + extra <= w.buf.length) return;
|
|
3512
|
+
const next = Buffer$1.alloc(Math.max(w.buf.length * 2, w.offset + extra));
|
|
3513
|
+
w.buf.copy(next, 0, 0, w.offset);
|
|
3514
|
+
w.buf = next;
|
|
3515
|
+
}
|
|
3516
|
+
function writeU32LE(w, value) {
|
|
3517
|
+
ensure(w, 4);
|
|
3518
|
+
w.buf.writeUInt32LE(value >>> 0, w.offset);
|
|
3519
|
+
w.offset += 4;
|
|
3520
|
+
}
|
|
3521
|
+
function writeU64LE(w, value) {
|
|
3522
|
+
if (value < 0n) {
|
|
3523
|
+
throw new RangeError(
|
|
3524
|
+
`writeU64LE: value must be non-negative, got ${value}`
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
ensure(w, 8);
|
|
3528
|
+
w.buf.writeBigUInt64LE(value, w.offset);
|
|
3529
|
+
w.offset += 8;
|
|
3530
|
+
}
|
|
3531
|
+
function writePubkey(w, bytes) {
|
|
3532
|
+
if (bytes.length !== 32) {
|
|
3533
|
+
throw new RangeError(`writePubkey: expected 32 bytes, got ${bytes.length}`);
|
|
3534
|
+
}
|
|
3535
|
+
ensure(w, 32);
|
|
3536
|
+
w.buf.set(bytes, w.offset);
|
|
3537
|
+
w.offset += 32;
|
|
3538
|
+
}
|
|
3539
|
+
function writeBytes(w, bytes) {
|
|
3540
|
+
writeU32LE(w, bytes.length);
|
|
3541
|
+
ensure(w, bytes.length);
|
|
3542
|
+
w.buf.set(bytes, w.offset);
|
|
3543
|
+
w.offset += bytes.length;
|
|
3544
|
+
}
|
|
3545
|
+
function withDiscriminator(discriminator, w) {
|
|
3546
|
+
const out = Buffer$1.alloc(discriminator.length + w.offset);
|
|
3547
|
+
out.set(discriminator, 0);
|
|
3548
|
+
w.buf.copy(out, discriminator.length, 0, w.offset);
|
|
3549
|
+
return out;
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
// src/solana/burn.ts
|
|
3553
|
+
var ANY_DESTINATION_CALLER2 = new PublicKey(new Uint8Array(32));
|
|
3554
|
+
var MAX_USDC_AMOUNT2 = 10000000n * 1000000n;
|
|
3555
|
+
function buildSolanaBurnIntent(input) {
|
|
3556
|
+
if (input.source.ecosystem !== "solana") {
|
|
3557
|
+
throw new InvalidInputError(
|
|
3558
|
+
"invalid_chain",
|
|
3559
|
+
`source must be a Solana chain, got ${input.source.ecosystem}`
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
if (input.amount <= 0n) {
|
|
3563
|
+
throw new InvalidInputError(
|
|
3564
|
+
"invalid_amount",
|
|
3565
|
+
`amount must be positive, got ${input.amount}`
|
|
3566
|
+
);
|
|
3567
|
+
}
|
|
3568
|
+
if (input.amount > MAX_USDC_AMOUNT2) {
|
|
3569
|
+
throw new InvalidInputError(
|
|
3570
|
+
"amount_too_large",
|
|
3571
|
+
`amount exceeds CCTP per-burn cap of 10M USDC`
|
|
3572
|
+
);
|
|
3573
|
+
}
|
|
3574
|
+
if (input.amount <= input.maxFee) {
|
|
3575
|
+
throw new InvalidInputError(
|
|
3576
|
+
"invalid_amount",
|
|
3577
|
+
"amount must exceed maxFee (program would revert MaxFeeMustBeLessThanAmount)"
|
|
3578
|
+
);
|
|
3579
|
+
}
|
|
3580
|
+
if (input.mintRecipientBytes32.length !== 32) {
|
|
3581
|
+
throw new InvalidInputError(
|
|
3582
|
+
"invalid_input",
|
|
3583
|
+
`mintRecipientBytes32 must be 32 bytes, got ${input.mintRecipientBytes32.length}`
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3586
|
+
const destCaller = input.destinationCallerBytes32 ?? new Uint8Array(32);
|
|
3587
|
+
if (destCaller.length !== 32) {
|
|
3588
|
+
throw new InvalidInputError(
|
|
3589
|
+
"invalid_input",
|
|
3590
|
+
`destinationCallerBytes32 must be 32 bytes, got ${destCaller.length}`
|
|
3591
|
+
);
|
|
3592
|
+
}
|
|
3593
|
+
if (input.hookData !== void 0 && input.hookData.length === 0) {
|
|
3594
|
+
throw new InvalidInputError(
|
|
3595
|
+
"invalid_input",
|
|
3596
|
+
"hookData must be non-empty (use the no-hook builder for plain burns)"
|
|
3597
|
+
);
|
|
3598
|
+
}
|
|
3599
|
+
const tmmProgram = new PublicKey(input.source.tokenMessengerMinter);
|
|
3600
|
+
const mtProgram = new PublicKey(input.source.messageTransmitter);
|
|
3601
|
+
const burnTokenMint = new PublicKey(input.source.usdc);
|
|
3602
|
+
const senderAuthorityPda = deriveSenderAuthority(tmmProgram);
|
|
3603
|
+
const denylistAccount = deriveDenylistAccount(tmmProgram, input.owner);
|
|
3604
|
+
const messageTransmitter = deriveMessageTransmitter(mtProgram);
|
|
3605
|
+
const tokenMessenger = deriveTokenMessenger(tmmProgram);
|
|
3606
|
+
const remoteTokenMessenger = deriveRemoteTokenMessenger(
|
|
3607
|
+
tmmProgram,
|
|
3608
|
+
input.destinationDomain
|
|
3609
|
+
);
|
|
3610
|
+
const tokenMinter = deriveTokenMinter(tmmProgram);
|
|
3611
|
+
const localToken = deriveLocalToken(tmmProgram, burnTokenMint);
|
|
3612
|
+
const eventAuthority = deriveEventAuthority(tmmProgram);
|
|
3613
|
+
const keys = [
|
|
3614
|
+
{ pubkey: input.owner, isSigner: true, isWritable: false },
|
|
3615
|
+
{ pubkey: input.owner, isSigner: true, isWritable: true },
|
|
3616
|
+
// event_rent_payer = owner
|
|
3617
|
+
{ pubkey: senderAuthorityPda, isSigner: false, isWritable: false },
|
|
3618
|
+
{ pubkey: input.ownerUsdcAta, isSigner: false, isWritable: true },
|
|
3619
|
+
{ pubkey: denylistAccount, isSigner: false, isWritable: false },
|
|
3620
|
+
{ pubkey: messageTransmitter, isSigner: false, isWritable: true },
|
|
3621
|
+
{ pubkey: tokenMessenger, isSigner: false, isWritable: false },
|
|
3622
|
+
{ pubkey: remoteTokenMessenger, isSigner: false, isWritable: false },
|
|
3623
|
+
{ pubkey: tokenMinter, isSigner: false, isWritable: false },
|
|
3624
|
+
{ pubkey: localToken, isSigner: false, isWritable: true },
|
|
3625
|
+
{ pubkey: burnTokenMint, isSigner: false, isWritable: true },
|
|
3626
|
+
{
|
|
3627
|
+
pubkey: input.messageSentEventDataKey,
|
|
3628
|
+
isSigner: true,
|
|
3629
|
+
isWritable: true
|
|
3630
|
+
},
|
|
3631
|
+
{ pubkey: mtProgram, isSigner: false, isWritable: false },
|
|
3632
|
+
{ pubkey: tmmProgram, isSigner: false, isWritable: false },
|
|
3633
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
3634
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
3635
|
+
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
|
|
3636
|
+
{ pubkey: tmmProgram, isSigner: false, isWritable: false }
|
|
3637
|
+
];
|
|
3638
|
+
const writer = createWriter(96);
|
|
3639
|
+
writeU64LE(writer, input.amount);
|
|
3640
|
+
writeU32LE(writer, input.destinationDomain);
|
|
3641
|
+
writePubkey(writer, input.mintRecipientBytes32);
|
|
3642
|
+
writePubkey(writer, destCaller);
|
|
3643
|
+
writeU64LE(writer, input.maxFee);
|
|
3644
|
+
writeU32LE(writer, input.minFinalityThreshold);
|
|
3645
|
+
if (input.hookData !== void 0) {
|
|
3646
|
+
writeBytes(writer, input.hookData);
|
|
3647
|
+
}
|
|
3648
|
+
const discriminator = input.hookData !== void 0 ? DISCRIMINATOR_DEPOSIT_FOR_BURN_WITH_HOOK : DISCRIMINATOR_DEPOSIT_FOR_BURN;
|
|
3649
|
+
const data = withDiscriminator(discriminator, writer);
|
|
3650
|
+
return {
|
|
3651
|
+
source: input.source,
|
|
3652
|
+
destinationDomain: input.destinationDomain,
|
|
3653
|
+
amount: input.amount,
|
|
3654
|
+
maxFee: input.maxFee,
|
|
3655
|
+
messageSentEventDataKey: input.messageSentEventDataKey,
|
|
3656
|
+
withHook: input.hookData !== void 0,
|
|
3657
|
+
instruction: {
|
|
3658
|
+
programId: tmmProgram,
|
|
3659
|
+
keys,
|
|
3660
|
+
data,
|
|
3661
|
+
ecosystem: "solana"
|
|
3662
|
+
}
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
function burnIntentToTransactionInstruction(intent) {
|
|
3666
|
+
return new TransactionInstruction({
|
|
3667
|
+
programId: intent.instruction.programId,
|
|
3668
|
+
keys: intent.instruction.keys,
|
|
3669
|
+
data: intent.instruction.data
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
function buildSolanaReceiveIntent(input) {
|
|
3673
|
+
if (input.destination.ecosystem !== "solana") {
|
|
3674
|
+
throw new InvalidInputError(
|
|
3675
|
+
"invalid_chain",
|
|
3676
|
+
`destination must be a Solana chain, got ${input.destination.ecosystem}`
|
|
3677
|
+
);
|
|
3678
|
+
}
|
|
3679
|
+
if (input.message.length < 44) {
|
|
3680
|
+
throw new InvalidInputError(
|
|
3681
|
+
"invalid_input",
|
|
3682
|
+
`message must be at least 44 bytes (Iris messages are much larger), got ${input.message.length}`
|
|
3683
|
+
);
|
|
3684
|
+
}
|
|
3685
|
+
if (input.attestation.length === 0) {
|
|
3686
|
+
throw new InvalidInputError(
|
|
3687
|
+
"invalid_input",
|
|
3688
|
+
"attestation must not be empty"
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
if (input.sourceTokenBytes32.length !== 32) {
|
|
3692
|
+
throw new InvalidInputError(
|
|
3693
|
+
"invalid_input",
|
|
3694
|
+
`sourceTokenBytes32 must be 32 bytes, got ${input.sourceTokenBytes32.length}`
|
|
3695
|
+
);
|
|
3696
|
+
}
|
|
3697
|
+
const tmmProgram = new PublicKey(input.destination.tokenMessengerMinter);
|
|
3698
|
+
const mtProgram = new PublicKey(input.destination.messageTransmitter);
|
|
3699
|
+
const localMint = new PublicKey(input.destination.usdc);
|
|
3700
|
+
const authorityPda = deriveMessageTransmitterAuthority(mtProgram, tmmProgram);
|
|
3701
|
+
const messageTransmitter = deriveMessageTransmitter(mtProgram);
|
|
3702
|
+
const usedNonce = deriveUsedNonce(mtProgram, input.message);
|
|
3703
|
+
const mtEventAuthority = deriveEventAuthority(mtProgram);
|
|
3704
|
+
const tokenMessenger = deriveTokenMessenger(tmmProgram);
|
|
3705
|
+
const remoteTokenMessenger = deriveRemoteTokenMessenger(
|
|
3706
|
+
tmmProgram,
|
|
3707
|
+
input.sourceDomain
|
|
3708
|
+
);
|
|
3709
|
+
const tokenMinter = deriveTokenMinter(tmmProgram);
|
|
3710
|
+
const localToken = deriveLocalToken(tmmProgram, localMint);
|
|
3711
|
+
const tokenPair = deriveTokenPair(
|
|
3712
|
+
tmmProgram,
|
|
3713
|
+
input.sourceDomain,
|
|
3714
|
+
input.sourceTokenBytes32
|
|
3715
|
+
);
|
|
3716
|
+
const custodyTokenAccount = deriveCustodyTokenAccount(tmmProgram, localMint);
|
|
3717
|
+
const tmmEventAuthority = deriveEventAuthority(tmmProgram);
|
|
3718
|
+
const keys = [
|
|
3719
|
+
{ pubkey: input.payer, isSigner: true, isWritable: true },
|
|
3720
|
+
{ pubkey: input.payer, isSigner: true, isWritable: false },
|
|
3721
|
+
// caller = payer
|
|
3722
|
+
{ pubkey: authorityPda, isSigner: false, isWritable: false },
|
|
3723
|
+
{ pubkey: messageTransmitter, isSigner: false, isWritable: false },
|
|
3724
|
+
{ pubkey: usedNonce, isSigner: false, isWritable: true },
|
|
3725
|
+
{ pubkey: tmmProgram, isSigner: false, isWritable: false },
|
|
3726
|
+
// receiver
|
|
3727
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
3728
|
+
{ pubkey: mtEventAuthority, isSigner: false, isWritable: false },
|
|
3729
|
+
{ pubkey: mtProgram, isSigner: false, isWritable: false },
|
|
3730
|
+
// Remaining accounts for TMM handle_receive
|
|
3731
|
+
{ pubkey: tokenMessenger, isSigner: false, isWritable: false },
|
|
3732
|
+
{ pubkey: remoteTokenMessenger, isSigner: false, isWritable: false },
|
|
3733
|
+
{ pubkey: tokenMinter, isSigner: false, isWritable: true },
|
|
3734
|
+
{ pubkey: localToken, isSigner: false, isWritable: true },
|
|
3735
|
+
{ pubkey: tokenPair, isSigner: false, isWritable: false },
|
|
3736
|
+
{ pubkey: input.feeRecipientUsdcAta, isSigner: false, isWritable: true },
|
|
3737
|
+
{ pubkey: input.recipientUsdcAta, isSigner: false, isWritable: true },
|
|
3738
|
+
{ pubkey: custodyTokenAccount, isSigner: false, isWritable: true },
|
|
3739
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
3740
|
+
{ pubkey: tmmEventAuthority, isSigner: false, isWritable: false },
|
|
3741
|
+
{ pubkey: tmmProgram, isSigner: false, isWritable: false }
|
|
3742
|
+
];
|
|
3743
|
+
const writer = createWriter(
|
|
3744
|
+
input.message.length + input.attestation.length + 16
|
|
3745
|
+
);
|
|
3746
|
+
writeBytes(writer, input.message);
|
|
3747
|
+
writeBytes(writer, input.attestation);
|
|
3748
|
+
const data = withDiscriminator(DISCRIMINATOR_RECEIVE_MESSAGE, writer);
|
|
3749
|
+
return {
|
|
3750
|
+
destination: input.destination,
|
|
3751
|
+
instruction: {
|
|
3752
|
+
programId: mtProgram,
|
|
3753
|
+
keys,
|
|
3754
|
+
data,
|
|
3755
|
+
ecosystem: "solana"
|
|
3756
|
+
}
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
function receiveIntentToTransactionInstruction(intent) {
|
|
3760
|
+
return new TransactionInstruction({
|
|
3761
|
+
programId: intent.instruction.programId,
|
|
3762
|
+
keys: intent.instruction.keys,
|
|
3763
|
+
data: intent.instruction.data
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3766
|
+
var FEE_RECIPIENT_OFFSET = 109;
|
|
3767
|
+
var FEE_RECIPIENT_END = 141;
|
|
3768
|
+
function parseTokenMessengerFeeRecipient(accountData) {
|
|
3769
|
+
if (accountData.length < FEE_RECIPIENT_END) {
|
|
3770
|
+
throw new InvalidInputError(
|
|
3771
|
+
"invalid_input",
|
|
3772
|
+
`TokenMessenger account too small (${accountData.length} bytes) \u2014 cannot read fee_recipient.`
|
|
3773
|
+
);
|
|
3774
|
+
}
|
|
3775
|
+
return new PublicKey(
|
|
3776
|
+
accountData.slice(FEE_RECIPIENT_OFFSET, FEE_RECIPIENT_END)
|
|
3777
|
+
);
|
|
3778
|
+
}
|
|
3779
|
+
|
|
3780
|
+
// src/index.ts
|
|
3781
|
+
var VERSION2 = "0.1.0";
|
|
3782
|
+
|
|
3783
|
+
export { ANY_DESTINATION_CALLER, Arbitrum, ArbitrumSepolia, Arc, ArcTestnet, Avalanche, AvalancheFuji, Base, BaseSepolia, CCTP_FORWARDER_STELLAR_MAINNET, CCTP_FORWARDER_STELLAR_TESTNET, CCTP_FORWARD_HOOK_DATA, CCTP_FORWARD_HOOK_DATA_LENGTH, CCTP_USDC_DECIMALS, CHAINS, CctpError, Codex, CodexTestnet, Cronos, CronosTestnet, DISCRIMINATOR_DEPOSIT_FOR_BURN, DISCRIMINATOR_DEPOSIT_FOR_BURN_WITH_HOOK, DISCRIMINATOR_RECEIVE_MESSAGE, EVM_MAINNET_CHAINS, EVM_TESTNET_CHAINS, Edge, EdgeTestnet, Ethereum, EthereumSepolia, FINALITY_FAST, FINALITY_STANDARD, HyperEVM, HyperEVMTestnet, IRIS_MAINNET_URL, IRIS_SANDBOX_URL, Injective, InjectiveTestnet, Ink, InkTestnet, InvalidAddressError, InvalidBytes32Error, InvalidInputError, IrisAbortedError, IrisError, IrisHttpError, IrisNetworkError, IrisParseError, IrisRateLimitedError, Linea, LineaSepolia, MESSAGE_TRANSMITTER_STELLAR_MAINNET, MESSAGE_TRANSMITTER_STELLAR_TESTNET, MESSAGE_TRANSMITTER_V2_EVM_MAINNET, MESSAGE_TRANSMITTER_V2_EVM_TESTNET, MESSAGE_TRANSMITTER_V2_SOLANA, MESSAGE_TRANSMITTER_V2_STARKNET_MAINNET, MESSAGE_TRANSMITTER_V2_STARKNET_SEPOLIA, Monad, MonadTestnet, Morph, MorphTestnet, Optimism, OptimismSepolia, Pharos, PharosTestnet, Plasma, PlasmaTestnet, Plume, PlumeTestnet, Polygon, PolygonAmoyTestnet, ProtocolError, ANY_DESTINATION_CALLER2 as SOLANA_ANY_DESTINATION_CALLER, SOLANA_CCTP_DOMAIN, SEEDS as SOLANA_CCTP_PDA_SEEDS, STARKNET_CCTP_DOMAIN, STARKNET_CHAIN_ID_MAINNET, STARKNET_CHAIN_ID_SEPOLIA, STELLAR_CCTP_DOMAIN, STELLAR_NETWORK_PASSPHRASE_MAINNET, STELLAR_NETWORK_PASSPHRASE_TESTNET, STELLAR_USDC_DECIMALS, Sei, SeiTestnet, Solana, SolanaDevnet, Sonic, SonicTestnet, Starknet, StarknetSepolia, Stellar, StellarTestnet, TOKEN_MESSENGER_MINTER_STELLAR_MAINNET, TOKEN_MESSENGER_MINTER_STELLAR_TESTNET, TOKEN_MESSENGER_MINTER_V2_SOLANA, TOKEN_MESSENGER_MINTER_V2_STARKNET_MAINNET, TOKEN_MESSENGER_MINTER_V2_STARKNET_SEPOLIA, TOKEN_MESSENGER_V2_EVM_MAINNET, TOKEN_MESSENGER_V2_EVM_TESTNET, TOKEN_MESSENGER_WITH_FEES_EVM_MAINNET, TOKEN_MESSENGER_WITH_FEES_EVM_TESTNET, USDC_ISSUER_STELLAR_MAINNET, USDC_ISSUER_STELLAR_TESTNET, USDC_SPL_DEVNET, USDC_SPL_MAINNET, USDC_STARKNET_MAINNET, USDC_STARKNET_SEPOLIA, Unichain, UnichainSepolia, UnknownChainError, UnsupportedError, VERSION2 as VERSION, WorldChain, WorldChainSepolia, XDC, XDCApothem, XLayer, XLayerTestnet, anchorDiscriminator, assertStarknetFelt, attestationSeconds, buildBurnIntent, buildBurnWithFeesIntent, buildForwarderHookData, buildMintIntent, buildSolanaBurnIntent, buildSolanaReceiveIntent, buildStarknetApproveCall, buildStarknetBurnIntent, buildStarknetReceiveIntent, buildStellarApproveInvocation, buildStellarBurnIntent, buildStellarForwarderHookData, buildStellarMintIntent, burnIntentToTransactionInstruction, bytes32HexToFelt, bytesToByteArrayFelts, canonicalToStellarSubunits, chainByDomain, chainByEvmChainId, chainsByNetwork, computeBurnFee, computeMaxFee, contractStrkeyToBytes32Hex, createIrisLimiter, decodeBurnMessage, decodeStrkeyToBytes32, defaultIrisLimiter, deriveCustodyTokenAccount, deriveDenylistAccount, deriveEventAuthority, deriveLocalToken, deriveMessageTransmitter, deriveMessageTransmitterAuthority, deriveRemoteTokenMessenger, deriveSenderAuthority, deriveTokenMessenger, deriveTokenMinter, deriveTokenPair, deriveUsedNonce, encodeContractStrkey, explorerAddressUrl, explorerTxUrl, feltToBigInt, feltToBytes32Hex, fetchIrisJson, fromBytes32, getBlockNumber, getBurnFees, getChain, getFastBurnAllowance, getGasPrice, getMessages, getSignedBurnQuote, getStandardMinFee, getStarknetTransactionStatus, getTransactionStatus, getUsdcBalance, hexToByteArrayFelts, irisBaseUrl, isAddress, isCctpError, isChainId, isEvmChain, isForwardDone, isForwarderHookData, isNonceUsed, isSolanaChain, isStarknetChain, isStarknetNonceUsed, isStellarChain, isStellarNonceUsed, isValidStarknetFelt, isValidStellarStrkey, messageTransmitterV2Abi, parseFeeBps, parseForwarderFee, parseTokenMessengerFeeRecipient, pickFeeTier, pickForwarderFee, pollBudget, pollMessages, reattest, receiveIntentToTransactionInstruction, selectForwarderFee, feltToString as starknetFeltToString, stellarBurnDust, stellarSubunitsToCanonical, strkeyTo32Bytes, strkeyType, toBytes32, tokenMessengerV2Abi, tokenMessengerWithFeesAbi, tryChainByEvmChainId, tryFromBytes32, u256ToFelts, usdcAbi };
|
|
3784
|
+
//# sourceMappingURL=index.js.map
|
|
3785
|
+
//# sourceMappingURL=index.js.map
|