@persistenceone/bridgekitty 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +232 -0
  3. package/dist/backends/across.d.ts +10 -0
  4. package/dist/backends/across.js +285 -0
  5. package/dist/backends/debridge.d.ts +11 -0
  6. package/dist/backends/debridge.js +380 -0
  7. package/dist/backends/lifi.d.ts +19 -0
  8. package/dist/backends/lifi.js +295 -0
  9. package/dist/backends/persistence.d.ts +86 -0
  10. package/dist/backends/persistence.js +642 -0
  11. package/dist/backends/relay.d.ts +11 -0
  12. package/dist/backends/relay.js +292 -0
  13. package/dist/backends/squid.d.ts +31 -0
  14. package/dist/backends/squid.js +476 -0
  15. package/dist/backends/types.d.ts +125 -0
  16. package/dist/backends/types.js +11 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +154 -0
  19. package/dist/routing/engine.d.ts +49 -0
  20. package/dist/routing/engine.js +336 -0
  21. package/dist/tools/check-status.d.ts +3 -0
  22. package/dist/tools/check-status.js +93 -0
  23. package/dist/tools/execute-bridge.d.ts +3 -0
  24. package/dist/tools/execute-bridge.js +428 -0
  25. package/dist/tools/get-chains.d.ts +3 -0
  26. package/dist/tools/get-chains.js +162 -0
  27. package/dist/tools/get-quote.d.ts +3 -0
  28. package/dist/tools/get-quote.js +534 -0
  29. package/dist/tools/get-tokens.d.ts +3 -0
  30. package/dist/tools/get-tokens.js +128 -0
  31. package/dist/tools/help.d.ts +2 -0
  32. package/dist/tools/help.js +204 -0
  33. package/dist/tools/multi-quote.d.ts +3 -0
  34. package/dist/tools/multi-quote.js +310 -0
  35. package/dist/tools/onboard.d.ts +3 -0
  36. package/dist/tools/onboard.js +218 -0
  37. package/dist/tools/wallet.d.ts +14 -0
  38. package/dist/tools/wallet.js +744 -0
  39. package/dist/tools/xprt-farm.d.ts +3 -0
  40. package/dist/tools/xprt-farm.js +1308 -0
  41. package/dist/tools/xprt-rewards.d.ts +2 -0
  42. package/dist/tools/xprt-rewards.js +177 -0
  43. package/dist/tools/xprt-staking.d.ts +2 -0
  44. package/dist/tools/xprt-staking.js +565 -0
  45. package/dist/utils/chains.d.ts +22 -0
  46. package/dist/utils/chains.js +154 -0
  47. package/dist/utils/circuit-breaker.d.ts +64 -0
  48. package/dist/utils/circuit-breaker.js +160 -0
  49. package/dist/utils/evm.d.ts +18 -0
  50. package/dist/utils/evm.js +46 -0
  51. package/dist/utils/fill-detector.d.ts +70 -0
  52. package/dist/utils/fill-detector.js +298 -0
  53. package/dist/utils/gas-estimator.d.ts +67 -0
  54. package/dist/utils/gas-estimator.js +340 -0
  55. package/dist/utils/sanitize-error.d.ts +23 -0
  56. package/dist/utils/sanitize-error.js +101 -0
  57. package/dist/utils/token-registry.d.ts +70 -0
  58. package/dist/utils/token-registry.js +669 -0
  59. package/dist/utils/tokens.d.ts +17 -0
  60. package/dist/utils/tokens.js +37 -0
  61. package/dist/utils/tx-simulator.d.ts +27 -0
  62. package/dist/utils/tx-simulator.js +105 -0
  63. package/package.json +75 -0
@@ -0,0 +1,669 @@
1
+ /**
2
+ * Verified Token Registry
3
+ *
4
+ * Security-first token symbol resolution. Only resolves to verified, canonical
5
+ * token addresses from official deployments. NEVER falls back to unverified tokens.
6
+ *
7
+ * Sources:
8
+ * - Circle's official USDC deployments
9
+ * - Tether's official USDT deployments
10
+ * - LI.FI's verified token list
11
+ * - CoinGecko verified contracts
12
+ * - Official bridge/wrapper contracts per chain
13
+ *
14
+ * If a token is not in this registry, resolution fails with a clear error.
15
+ * Agents can always pass raw 0x addresses to bypass the registry.
16
+ */
17
+ import { getChainName, PERSISTENCE_CHAIN_ID, COSMOSHUB_CHAIN_ID, SOLANA_CHAIN_ID } from "./chains.js";
18
+ // ─── Constants ──────────────────────────────────────────────────────────────
19
+ const NATIVE = "0x0000000000000000000000000000000000000000";
20
+ // ─── Chain IDs ──────────────────────────────────────────────────────────────
21
+ // Documented here for reference:
22
+ // 1 = Ethereum
23
+ // 10 = Optimism
24
+ // 56 = BNB Chain (BSC)
25
+ // 137 = Polygon
26
+ // 324 = zkSync Era
27
+ // 5000 = Mantle
28
+ // 8453 = Base
29
+ // 34443 = Mode
30
+ // 42161 = Arbitrum
31
+ // 43114 = Avalanche
32
+ // 59144 = Linea
33
+ // 81457 = Blast
34
+ // 534352 = Scroll
35
+ // 7777777 = Zora
36
+ // ─── Verified Token Registry ────────────────────────────────────────────────
37
+ export const VERIFIED_TOKENS = [
38
+ // ── Native Tokens ──────────────────────────────────────────────────────
39
+ {
40
+ symbol: "ETH",
41
+ name: "Ethereum",
42
+ decimals: 18,
43
+ addresses: {
44
+ 1: NATIVE,
45
+ 10: NATIVE,
46
+ 42161: NATIVE,
47
+ 8453: NATIVE,
48
+ 59144: NATIVE,
49
+ 534352: NATIVE,
50
+ 324: NATIVE,
51
+ 81457: NATIVE,
52
+ 7777777: NATIVE,
53
+ 34443: NATIVE,
54
+ },
55
+ },
56
+ {
57
+ symbol: "BNB",
58
+ name: "BNB",
59
+ decimals: 18,
60
+ addresses: {
61
+ 56: NATIVE,
62
+ },
63
+ },
64
+ {
65
+ symbol: "MATIC",
66
+ name: "Polygon",
67
+ decimals: 18,
68
+ addresses: {
69
+ 137: NATIVE,
70
+ },
71
+ },
72
+ {
73
+ // POL is the rebranded MATIC — same native token on Polygon
74
+ symbol: "POL",
75
+ name: "Polygon (POL)",
76
+ decimals: 18,
77
+ addresses: {
78
+ 137: NATIVE,
79
+ },
80
+ },
81
+ {
82
+ symbol: "AVAX",
83
+ name: "Avalanche",
84
+ decimals: 18,
85
+ addresses: {
86
+ 43114: NATIVE,
87
+ },
88
+ },
89
+ {
90
+ symbol: "MNT",
91
+ name: "Mantle",
92
+ decimals: 18,
93
+ addresses: {
94
+ 5000: NATIVE,
95
+ },
96
+ },
97
+ // ── Stablecoins ────────────────────────────────────────────────────────
98
+ {
99
+ // Circle's official native USDC deployments
100
+ symbol: "USDC",
101
+ name: "USD Coin",
102
+ decimals: 6,
103
+ decimalOverrides: { 56: 18 }, // BSC Binance-Peg uses 18 decimals
104
+ addresses: {
105
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Ethereum
106
+ 10: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", // Optimism (native)
107
+ 56: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", // BSC (Binance-Peg, 18 decimals!)
108
+ 137: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", // Polygon (native)
109
+ 42161: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // Arbitrum (native)
110
+ 8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base (native)
111
+ 43114: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", // Avalanche (native)
112
+ [SOLANA_CHAIN_ID]: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Solana (native)
113
+ },
114
+ },
115
+ {
116
+ // Bridged USDC.e (older, being deprecated in favor of native USDC)
117
+ symbol: "USDC.E",
118
+ name: "Bridged USD Coin",
119
+ decimals: 6,
120
+ addresses: {
121
+ 10: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // Optimism
122
+ 137: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // Polygon
123
+ 42161: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // Arbitrum
124
+ 43114: "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664", // Avalanche
125
+ },
126
+ },
127
+ {
128
+ symbol: "USDT",
129
+ name: "Tether USD",
130
+ decimals: 6,
131
+ decimalOverrides: { 56: 18 }, // BSC Binance-Peg uses 18 decimals
132
+ addresses: {
133
+ 1: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // Ethereum
134
+ 10: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", // Optimism
135
+ 56: "0x55d398326f99059fF775485246999027B3197955", // BSC (Binance-Peg, 18 decimals!)
136
+ 137: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", // Polygon
137
+ 42161: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", // Arbitrum
138
+ 43114: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", // Avalanche
139
+ 8453: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2", // Base
140
+ [SOLANA_CHAIN_ID]: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // Solana
141
+ },
142
+ },
143
+ {
144
+ symbol: "DAI",
145
+ name: "Dai Stablecoin",
146
+ decimals: 18,
147
+ addresses: {
148
+ 1: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // Ethereum
149
+ 10: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", // Optimism
150
+ 137: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", // Polygon
151
+ 42161: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", // Arbitrum
152
+ 43114: "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70", // Avalanche
153
+ 8453: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", // Base
154
+ },
155
+ },
156
+ {
157
+ symbol: "USDS",
158
+ name: "USDS (Sky Dollar)",
159
+ decimals: 18,
160
+ addresses: {
161
+ 1: "0xdC035D45d973E3EC169d2276DDab16f1e407384F", // Ethereum
162
+ },
163
+ },
164
+ {
165
+ symbol: "FRAX",
166
+ name: "Frax",
167
+ decimals: 18,
168
+ addresses: {
169
+ 1: "0x853d955aCEf822Db058eb8505911ED77F175b99e", // Ethereum
170
+ 10: "0x2E3D870790dC77A83DD1d22184567a97112523ab", // Optimism
171
+ 137: "0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89", // Polygon
172
+ 42161: "0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F", // Arbitrum
173
+ 43114: "0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64", // Avalanche
174
+ },
175
+ },
176
+ {
177
+ symbol: "GHO",
178
+ name: "GHO (Aave)",
179
+ decimals: 18,
180
+ addresses: {
181
+ 1: "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f", // Ethereum
182
+ 42161: "0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33", // Arbitrum
183
+ },
184
+ },
185
+ // ── Wrapped ETH ────────────────────────────────────────────────────────
186
+ {
187
+ symbol: "WETH",
188
+ name: "Wrapped Ether",
189
+ decimals: 18,
190
+ addresses: {
191
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // Ethereum
192
+ 10: "0x4200000000000000000000000000000000000006", // Optimism
193
+ 56: "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", // BSC (Binance-Peg ETH)
194
+ 137: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", // Polygon
195
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Arbitrum
196
+ 8453: "0x4200000000000000000000000000000000000006", // Base
197
+ 43114: "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB", // Avalanche (WETH.e)
198
+ 59144: "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f", // Linea
199
+ 534352: "0x5300000000000000000000000000000000000004", // Scroll
200
+ 324: "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", // zkSync
201
+ 81457: "0x4300000000000000000000000000000000000004", // Blast
202
+ 5000: "0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111", // Mantle (WETH)
203
+ },
204
+ },
205
+ // ── BTC Variants ───────────────────────────────────────────────────────
206
+ {
207
+ symbol: "WBTC",
208
+ name: "Wrapped Bitcoin",
209
+ decimals: 8,
210
+ addresses: {
211
+ 1: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", // Ethereum
212
+ 10: "0x68f180fcCe6836688e9084f035309E29Bf0A2095", // Optimism
213
+ 137: "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6", // Polygon
214
+ 42161: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", // Arbitrum
215
+ 43114: "0x50b7545627a5162F82A992c33b87aDc75187B218", // Avalanche
216
+ },
217
+ },
218
+ {
219
+ symbol: "BTCB",
220
+ name: "Binance-Peg BTCB",
221
+ decimals: 18,
222
+ addresses: {
223
+ 56: "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", // BSC
224
+ },
225
+ },
226
+ {
227
+ symbol: "CBBTC",
228
+ name: "Coinbase Wrapped BTC",
229
+ decimals: 8,
230
+ addresses: {
231
+ 1: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", // Ethereum
232
+ 8453: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", // Base
233
+ },
234
+ },
235
+ {
236
+ symbol: "TBTC",
237
+ name: "Threshold BTC",
238
+ decimals: 18,
239
+ addresses: {
240
+ 1: "0x18084fbA666a33d37592fA2633fD49a74DD93a88", // Ethereum
241
+ 10: "0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40", // Optimism
242
+ 137: "0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b", // Polygon
243
+ 42161: "0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40", // Arbitrum
244
+ 8453: "0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b", // Base
245
+ },
246
+ },
247
+ // ── Liquid Staking Tokens ──────────────────────────────────────────────
248
+ {
249
+ symbol: "STETH",
250
+ name: "Lido Staked ETH",
251
+ decimals: 18,
252
+ addresses: {
253
+ 1: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", // Ethereum
254
+ },
255
+ },
256
+ {
257
+ symbol: "WSTETH",
258
+ name: "Lido Wrapped Staked ETH",
259
+ decimals: 18,
260
+ addresses: {
261
+ 1: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", // Ethereum
262
+ 10: "0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb", // Optimism
263
+ 137: "0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD", // Polygon
264
+ 42161: "0x5979D7b546E38E9Ab8a45CfA782CAa3c8539b40C", // Arbitrum
265
+ 8453: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", // Base
266
+ 534352: "0xf610A9dfB7C89644979b4A0f27063E9e7d7Cda32", // Scroll
267
+ 59144: "0xB5beDd42000b71FddE22D3eE8a79Bd49A568fC8F", // Linea
268
+ 5000: "0x636D4073738C071326Aa70fB30dB8e8F21aFabca", // Mantle
269
+ },
270
+ },
271
+ {
272
+ symbol: "RETH",
273
+ name: "Rocket Pool ETH",
274
+ decimals: 18,
275
+ addresses: {
276
+ 1: "0xae78736Cd615f374D3085123A210448E74Fc6393", // Ethereum
277
+ 10: "0x9Bcef72be871e61ED4fBbc7630889beE758eb81D", // Optimism
278
+ 42161: "0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8", // Arbitrum
279
+ 8453: "0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c", // Base
280
+ },
281
+ },
282
+ {
283
+ symbol: "CBETH",
284
+ name: "Coinbase Staked ETH",
285
+ decimals: 18,
286
+ addresses: {
287
+ 1: "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", // Ethereum
288
+ 10: "0xadDb6A0412DE1BA0F936DCaeb8Aaa24578dcF3B2", // Optimism
289
+ 8453: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", // Base
290
+ 42161: "0x1DEBd73E752bEaF79865Fd6446b0c970EaE7732f", // Arbitrum
291
+ },
292
+ },
293
+ // ── Wrapped Native Tokens (non-ETH) ───────────────────────────────────
294
+ {
295
+ symbol: "WBNB",
296
+ name: "Wrapped BNB",
297
+ decimals: 18,
298
+ addresses: {
299
+ 56: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", // BSC
300
+ },
301
+ },
302
+ {
303
+ symbol: "WMATIC",
304
+ name: "Wrapped MATIC",
305
+ decimals: 18,
306
+ addresses: {
307
+ 137: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", // Polygon
308
+ },
309
+ },
310
+ {
311
+ symbol: "WAVAX",
312
+ name: "Wrapped AVAX",
313
+ decimals: 18,
314
+ addresses: {
315
+ 43114: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", // Avalanche
316
+ },
317
+ },
318
+ // ── L2 Governance Tokens ───────────────────────────────────────────────
319
+ {
320
+ symbol: "ARB",
321
+ name: "Arbitrum",
322
+ decimals: 18,
323
+ addresses: {
324
+ 42161: "0x912CE59144191C1204E64559FE8253a0e49E6548", // Arbitrum
325
+ 1: "0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1", // Ethereum
326
+ },
327
+ },
328
+ {
329
+ symbol: "OP",
330
+ name: "Optimism",
331
+ decimals: 18,
332
+ addresses: {
333
+ 10: "0x4200000000000000000000000000000000000042", // Optimism
334
+ },
335
+ },
336
+ // ── DeFi Blue Chips ────────────────────────────────────────────────────
337
+ {
338
+ symbol: "LINK",
339
+ name: "Chainlink",
340
+ decimals: 18,
341
+ addresses: {
342
+ 1: "0x514910771AF9Ca656af840dff83E8264EcF986CA", // Ethereum
343
+ 10: "0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6", // Optimism
344
+ 56: "0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD", // BSC
345
+ 137: "0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39", // Polygon
346
+ 42161: "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", // Arbitrum
347
+ 43114: "0x5947BB275c521040051D82396192181b413227A3", // Avalanche
348
+ 8453: "0x88Fb150BDc53A65fe94Dea0c9BA0a6dAf8C6e196", // Base
349
+ },
350
+ },
351
+ {
352
+ symbol: "UNI",
353
+ name: "Uniswap",
354
+ decimals: 18,
355
+ addresses: {
356
+ 1: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", // Ethereum
357
+ 10: "0x6fd9d7AD17242c41f7131d257212c54A0e816691", // Optimism
358
+ 137: "0xb33EaAd8d922B1083446DC23f610c2567fB5180f", // Polygon
359
+ 42161: "0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0", // Arbitrum
360
+ 56: "0xBf5140A22578168FD562DCcF235E5D43A02ce9B1", // BSC
361
+ 43114: "0x8eBAf22B6F053dFFeaf46f4Dd9eFA95D89ba8580", // Avalanche
362
+ 8453: "0xc3De830EA07524a0761646a6a4e4be0e114a3C83", // Base
363
+ },
364
+ },
365
+ {
366
+ symbol: "AAVE",
367
+ name: "Aave",
368
+ decimals: 18,
369
+ addresses: {
370
+ 1: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", // Ethereum
371
+ 10: "0x76FB31fb4af56892A25e32cFC43De717950c9278", // Optimism
372
+ 137: "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", // Polygon
373
+ 42161: "0xba5DdD1f9d7F570dc94a51479a000E3BCE967196", // Arbitrum
374
+ 43114: "0x63a72806098Bd3D9520cC43356dD78afe5D386D9", // Avalanche
375
+ },
376
+ },
377
+ {
378
+ symbol: "MKR",
379
+ name: "Maker",
380
+ decimals: 18,
381
+ addresses: {
382
+ 1: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", // Ethereum
383
+ },
384
+ },
385
+ {
386
+ symbol: "CRV",
387
+ name: "Curve DAO Token",
388
+ decimals: 18,
389
+ addresses: {
390
+ 1: "0xD533a949740bb3306d119CC777fa900bA034cd52", // Ethereum
391
+ 10: "0x0994206dfE8De6Ec6920FF4D779B0d950605Fb53", // Optimism
392
+ 137: "0x172370d5Cd63279eFa6d502DAB29171933a610AF", // Polygon
393
+ 42161: "0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978", // Arbitrum
394
+ 43114: "0x249848BeCA43aC405b8102Ec90Dd5F22CA513c06", // Avalanche
395
+ },
396
+ },
397
+ {
398
+ symbol: "LDO",
399
+ name: "Lido DAO",
400
+ decimals: 18,
401
+ addresses: {
402
+ 1: "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32", // Ethereum
403
+ 10: "0xFdb794692724153d1488CcdBE0C56c0C1EE1fabf", // Optimism
404
+ 137: "0xC3C7d422809852031b44ab29EEC9F1EfF2A58756", // Polygon
405
+ 42161: "0x13Ad51ed4F1B7e9Dc168d8a00cB3f4dDD85EfA60", // Arbitrum
406
+ },
407
+ },
408
+ {
409
+ symbol: "SNX",
410
+ name: "Synthetix",
411
+ decimals: 18,
412
+ addresses: {
413
+ 1: "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", // Ethereum
414
+ 10: "0x8700dAec35aF8Ff88c16BdF0418774CB3D7599B4", // Optimism
415
+ },
416
+ },
417
+ {
418
+ symbol: "COMP",
419
+ name: "Compound",
420
+ decimals: 18,
421
+ addresses: {
422
+ 1: "0xc00e94Cb662C3520282E6f5717214004A7f26888", // Ethereum
423
+ 137: "0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c", // Polygon
424
+ 42161: "0x354A6dA3fcde098F8389cad84b0182725c6C91dE", // Arbitrum
425
+ 8453: "0x9e1028F5F1D5eDE59748FFceE5532509976840E0", // Base
426
+ },
427
+ },
428
+ // ── Meme / High Cap ────────────────────────────────────────────────────
429
+ {
430
+ symbol: "PEPE",
431
+ name: "Pepe",
432
+ decimals: 18,
433
+ addresses: {
434
+ 1: "0x6982508145454Ce325dDbE47a25d4ec3d2311933", // Ethereum
435
+ 42161: "0x25d887Ce7a35172C62FeBFD67a1856F20FaEbB00", // Arbitrum
436
+ },
437
+ },
438
+ {
439
+ symbol: "SHIB",
440
+ name: "Shiba Inu",
441
+ decimals: 18,
442
+ addresses: {
443
+ 1: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", // Ethereum
444
+ },
445
+ },
446
+ {
447
+ symbol: "DOGE",
448
+ name: "Dogecoin (Bridged)",
449
+ decimals: 8,
450
+ addresses: {
451
+ 56: "0xbA2aE424d960c26247Dd6c32edC70B295c744C43", // BSC (Binance-Peg)
452
+ },
453
+ },
454
+ // ── Wrapped / Misc ─────────────────────────────────────────────────────
455
+ {
456
+ symbol: "SUSHI",
457
+ name: "SushiSwap",
458
+ decimals: 18,
459
+ addresses: {
460
+ 1: "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", // Ethereum
461
+ 137: "0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a", // Polygon
462
+ 42161: "0xd4d42F0b6DEF4CE0383636770eF773390d85c61A", // Arbitrum
463
+ },
464
+ },
465
+ {
466
+ symbol: "RPL",
467
+ name: "Rocket Pool",
468
+ decimals: 18,
469
+ addresses: {
470
+ 1: "0xD33526068D116cE69F19A9ee46F0bd304F21A51f", // Ethereum
471
+ 42161: "0xB766039cc6DB368759C1E56B79AFfE831545A950", // Arbitrum
472
+ },
473
+ },
474
+ {
475
+ symbol: "PENDLE",
476
+ name: "Pendle",
477
+ decimals: 18,
478
+ addresses: {
479
+ 1: "0x808507121B80c02388fAd14726482e061B8da827", // Ethereum
480
+ 42161: "0x0c880f6761F1af8d9Aa9C466984b80DAb9a8c9e8", // Arbitrum
481
+ },
482
+ },
483
+ {
484
+ symbol: "ENA",
485
+ name: "Ethena",
486
+ decimals: 18,
487
+ addresses: {
488
+ 1: "0x57e114B691Db790C35207b2e685D4A43181e6061", // Ethereum
489
+ },
490
+ },
491
+ {
492
+ symbol: "USDE",
493
+ name: "USDe (Ethena)",
494
+ decimals: 18,
495
+ addresses: {
496
+ 1: "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3", // Ethereum
497
+ 42161: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", // Arbitrum
498
+ 8453: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", // Base
499
+ },
500
+ },
501
+ // ── Cosmos Ecosystem Tokens ──────────────────────────────────────────
502
+ {
503
+ symbol: "XPRT",
504
+ name: "Persistence",
505
+ decimals: 6,
506
+ addresses: {
507
+ // Persistence chain uses uxprt denom (micro-XPRT, 6 decimals)
508
+ [PERSISTENCE_CHAIN_ID]: "uxprt",
509
+ },
510
+ },
511
+ {
512
+ symbol: "ATOM",
513
+ name: "Cosmos Hub",
514
+ decimals: 6,
515
+ addresses: {
516
+ // Cosmos Hub uses uatom denom
517
+ [COSMOSHUB_CHAIN_ID]: "uatom",
518
+ },
519
+ },
520
+ // ── Solana Tokens ──────────────────────────────────────────────────────
521
+ {
522
+ symbol: "SOL",
523
+ name: "Solana",
524
+ decimals: 9,
525
+ addresses: {
526
+ [SOLANA_CHAIN_ID]: "So11111111111111111111111111111111111111112", // Wrapped SOL mint
527
+ },
528
+ },
529
+ ];
530
+ // ─── Indexes (built once at module load) ────────────────────────────────────
531
+ /** Map of UPPERCASE symbol → array of VerifiedToken entries */
532
+ const symbolIndex = new Map();
533
+ /** Map of lowercase(address):chainId → VerifiedToken */
534
+ const addressIndex = new Map();
535
+ function buildIndexes() {
536
+ for (const token of VERIFIED_TOKENS) {
537
+ const key = token.symbol.toUpperCase();
538
+ const existing = symbolIndex.get(key) ?? [];
539
+ existing.push(token);
540
+ symbolIndex.set(key, existing);
541
+ for (const [chainIdStr, address] of Object.entries(token.addresses)) {
542
+ const addrKey = `${address.toLowerCase()}:${chainIdStr}`;
543
+ addressIndex.set(addrKey, token);
544
+ }
545
+ }
546
+ }
547
+ buildIndexes();
548
+ // ─── Resolution Functions ───────────────────────────────────────────────────
549
+ /**
550
+ * Resolve a token input (symbol or 0x address) to a verified address.
551
+ *
552
+ * Security rules:
553
+ * - If input is a 0x address: pass through unchanged (with known decimals if available)
554
+ * - If input is a symbol: ONLY resolve to verified canonical addresses
555
+ * - If symbol is unknown: return error with guidance
556
+ * - If symbol is ambiguous (multiple tokens): return error listing options
557
+ * - NEVER falls back to unverified tokens
558
+ */
559
+ export function resolveToken(input, chainId) {
560
+ const trimmed = input.trim();
561
+ // 0x address passthrough — agent knows what they're doing
562
+ if (trimmed.startsWith("0x") && trimmed.length === 42) {
563
+ // Normalize alternative native token address (0xEeEe...) to canonical zero address
564
+ let normalizedAddr = trimmed;
565
+ if (trimmed.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") {
566
+ normalizedAddr = NATIVE;
567
+ }
568
+ const known = addressIndex.get(`${normalizedAddr.toLowerCase()}:${chainId}`);
569
+ const decimals = known
570
+ ? (known.decimalOverrides?.[chainId] ?? known.decimals)
571
+ : 18;
572
+ return {
573
+ ok: true,
574
+ address: normalizedAddr,
575
+ decimals,
576
+ symbol: known?.symbol ?? "UNKNOWN",
577
+ };
578
+ }
579
+ // Symbol resolution — security-critical path
580
+ const upper = trimmed.toUpperCase();
581
+ const candidates = symbolIndex.get(upper);
582
+ if (!candidates || candidates.length === 0) {
583
+ const chainName = getChainName(chainId);
584
+ return {
585
+ ok: false,
586
+ error: `Unknown token symbol '${trimmed}' on ${chainName} (chain ${chainId}). Use bridge_tokens to search, or provide the contract address directly.`,
587
+ };
588
+ }
589
+ // Find all candidates that have an address on this chain
590
+ const matches = [];
591
+ for (const token of candidates) {
592
+ const address = token.addresses[chainId];
593
+ if (address) {
594
+ matches.push({ token, address });
595
+ }
596
+ }
597
+ if (matches.length === 0) {
598
+ const chainName = getChainName(chainId);
599
+ const availableChains = candidates
600
+ .flatMap((t) => Object.keys(t.addresses).map(Number))
601
+ .filter((id, i, arr) => arr.indexOf(id) === i)
602
+ .map((id) => `${getChainName(id)} (${id})`)
603
+ .join(", ");
604
+ return {
605
+ ok: false,
606
+ error: `Token '${trimmed}' is not available on ${chainName} (chain ${chainId}). Available on: ${availableChains}. Use bridge_tokens to search for alternatives, or provide a contract address directly.`,
607
+ };
608
+ }
609
+ if (matches.length > 1) {
610
+ const chainName = getChainName(chainId);
611
+ const options = matches
612
+ .map((m) => ` • ${m.token.symbol} (${m.token.name}): ${m.address}`)
613
+ .join("\n");
614
+ return {
615
+ ok: false,
616
+ error: `Ambiguous token symbol '${trimmed}' on ${chainName} (chain ${chainId}). Multiple verified tokens found:\n${options}\nSpecify the contract address directly to disambiguate.`,
617
+ };
618
+ }
619
+ // Exactly one match — verified resolution
620
+ const match = matches[0];
621
+ const decimals = match.token.decimalOverrides?.[chainId] ?? match.token.decimals;
622
+ return {
623
+ ok: true,
624
+ address: match.address,
625
+ decimals,
626
+ symbol: match.token.symbol,
627
+ };
628
+ }
629
+ /**
630
+ * Look up a verified token by address on a specific chain.
631
+ * Returns null if not in the registry (the token may still be valid, just not curated).
632
+ */
633
+ export function lookupByAddress(address, chainId) {
634
+ return addressIndex.get(`${address.toLowerCase()}:${chainId}`) ?? null;
635
+ }
636
+ /**
637
+ * Get all verified tokens available on a specific chain.
638
+ */
639
+ export function getVerifiedTokensForChain(chainId) {
640
+ const results = [];
641
+ for (const token of VERIFIED_TOKENS) {
642
+ const address = token.addresses[chainId];
643
+ if (address) {
644
+ const decimals = token.decimalOverrides?.[chainId] ?? token.decimals;
645
+ results.push({
646
+ symbol: token.symbol,
647
+ name: token.name,
648
+ address,
649
+ decimals,
650
+ });
651
+ }
652
+ }
653
+ return results;
654
+ }
655
+ /**
656
+ * Get the total number of unique tokens in the registry.
657
+ */
658
+ export function getRegistryStats() {
659
+ const chains = new Set();
660
+ for (const token of VERIFIED_TOKENS) {
661
+ for (const chainId of Object.keys(token.addresses)) {
662
+ chains.add(Number(chainId));
663
+ }
664
+ }
665
+ return {
666
+ tokenCount: VERIFIED_TOKENS.length,
667
+ chainCount: chains.size,
668
+ };
669
+ }
@@ -0,0 +1,17 @@
1
+ export { resolveToken, lookupByAddress, type TokenResolveResult } from "./token-registry.js";
2
+ /**
3
+ * Resolve a token symbol or address to { address, decimals }.
4
+ *
5
+ * Returns null if:
6
+ * - Symbol is unknown in the verified registry for this chain
7
+ * - Address is already a 0x address (still returns result with known/default decimals)
8
+ *
9
+ * @deprecated Use `resolveToken()` for better error messages.
10
+ * This function is kept for backward compatibility.
11
+ */
12
+ export declare function resolveTokenAddress(symbol: string, chainId: number): {
13
+ address: string;
14
+ decimals: number;
15
+ } | null;
16
+ export declare function formatTokenAmount(amountRaw: string, decimals: number): string;
17
+ export declare function parseTokenAmount(amount: string, decimals: number): string;