@stablechain/sdk 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/dist/index.cjs ADDED
@@ -0,0 +1,893 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/stable.ts
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+ var _viem = require('viem');
10
+ var _chains = require('viem/chains');
11
+
12
+ // src/errors.ts
13
+
14
+ var StableError = class extends _viem.BaseError {
15
+ };
16
+ var StableValidationError = class extends StableError {
17
+ constructor(field, value, reason, opts) {
18
+ super(reason, {
19
+ name: "StableValidationError",
20
+ cause: _optionalChain([opts, 'optionalAccess', _ => _.cause]),
21
+ metaMessages: [`Field: ${field}`, `Value: ${formatValue(value)}`]
22
+ });
23
+ this.field = field;
24
+ this.value = value;
25
+ }
26
+ };
27
+ var StableQuoteError = class extends StableError {
28
+ constructor(args) {
29
+ const meta = [`Provider: ${args.provider}`];
30
+ if (args.httpStatus !== void 0) meta.push(`HTTP status: ${args.httpStatus}`);
31
+ if (args.providerCode) meta.push(`Provider code: ${args.providerCode}`);
32
+ super(args.shortMessage, {
33
+ name: "StableQuoteError",
34
+ cause: args.cause,
35
+ metaMessages: meta
36
+ });
37
+ this.provider = args.provider;
38
+ this.httpStatus = args.httpStatus;
39
+ this.providerCode = args.providerCode;
40
+ this.body = args.body;
41
+ }
42
+ };
43
+ var StableTransactionError = class extends StableError {
44
+ constructor(args) {
45
+ const meta = [`Phase: ${args.phase}`];
46
+ if (args.chainId !== void 0) meta.push(`Chain ID: ${args.chainId}`);
47
+ if (args.txHash) meta.push(`Tx hash: ${args.txHash}`);
48
+ if (args.revertReason) meta.push(`Revert reason: ${args.revertReason}`);
49
+ super(args.shortMessage, {
50
+ name: "StableTransactionError",
51
+ cause: args.cause,
52
+ metaMessages: meta
53
+ });
54
+ this.phase = args.phase;
55
+ this.txHash = args.txHash;
56
+ this.chainId = args.chainId;
57
+ this.revertReason = args.revertReason;
58
+ }
59
+ };
60
+ var StableNetworkError = class extends StableError {
61
+ constructor(args) {
62
+ super(args.shortMessage, {
63
+ name: "StableNetworkError",
64
+ cause: args.cause,
65
+ metaMessages: args.url ? [`URL: ${args.url}`] : void 0
66
+ });
67
+ this.url = args.url;
68
+ }
69
+ };
70
+ function formatValue(v) {
71
+ if (typeof v === "bigint") return `${v}n`;
72
+ if (typeof v === "string") return JSON.stringify(v);
73
+ if (v === null) return "null";
74
+ if (v === void 0) return "undefined";
75
+ try {
76
+ return JSON.stringify(v);
77
+ } catch (e) {
78
+ return String(v);
79
+ }
80
+ }
81
+
82
+ // src/services/layerzero.ts
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+ // src/helpers/chain.ts
96
+
97
+ function makeChain(id, rpc) {
98
+ return _viem.defineChain.call(void 0, {
99
+ id,
100
+ name: "chain",
101
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
102
+ rpcUrls: { default: { http: [rpc] } }
103
+ });
104
+ }
105
+
106
+ // src/types.ts
107
+ var Chain = /* @__PURE__ */ ((Chain2) => {
108
+ Chain2["Sepolia"] = "sepolia";
109
+ Chain2["StableTestnet"] = "stable-testnet";
110
+ Chain2["Stable"] = "stable";
111
+ Chain2["Ethereum"] = "ethereum";
112
+ Chain2["Arbitrum"] = "arbitrum";
113
+ Chain2["Ink"] = "ink";
114
+ Chain2["Bera"] = "bera";
115
+ Chain2["MegaETH"] = "megaeth";
116
+ Chain2["Base"] = "base";
117
+ Chain2["BSC"] = "bsc";
118
+ Chain2["HyperEVM"] = "hyperevm";
119
+ return Chain2;
120
+ })(Chain || {});
121
+ var Network = /* @__PURE__ */ ((Network2) => {
122
+ Network2["Mainnet"] = "mainnet";
123
+ Network2["Testnet"] = "testnet";
124
+ return Network2;
125
+ })(Network || {});
126
+
127
+ // src/chains.ts
128
+ var CHAIN_CONFIGS = {
129
+ // ─── Testnet ──────────────────────────────────────────────────────────────
130
+ ["sepolia" /* Sepolia */]: {
131
+ id: 11155111,
132
+ rpc: "https://ethereum-sepolia-rpc.publicnode.com",
133
+ usdt: "0xc4DCC311c028e341fd8602D8eB89c5de94625927",
134
+ decimals: 18
135
+ },
136
+ ["stable-testnet" /* StableTestnet */]: {
137
+ id: 2201,
138
+ rpc: "https://rpc.testnet.stable.xyz",
139
+ usdt: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9",
140
+ decimals: 6
141
+ },
142
+ // ─── Mainnet ──────────────────────────────────────────────────────────────
143
+ ["stable" /* Stable */]: {
144
+ id: 988,
145
+ rpc: "https://rpc.stable.xyz",
146
+ usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
147
+ decimals: 6
148
+ },
149
+ ["ethereum" /* Ethereum */]: {
150
+ id: 1,
151
+ rpc: "https://ethereum-rpc.publicnode.com",
152
+ usdt: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
153
+ decimals: 6
154
+ },
155
+ ["arbitrum" /* Arbitrum */]: {
156
+ id: 42161,
157
+ rpc: "https://arbitrum-one-rpc.publicnode.com",
158
+ usdt: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92",
159
+ decimals: 6
160
+ },
161
+ ["ink" /* Ink */]: {
162
+ id: 57073,
163
+ rpc: "https://rpc-gel.inkonchain.com",
164
+ usdt: "0x0200C29006150606B650577BBE7B6248F58470c1",
165
+ decimals: 6
166
+ },
167
+ ["bera" /* Bera */]: {
168
+ id: 80094,
169
+ rpc: "https://rpc.berachain.com",
170
+ usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
171
+ decimals: 6
172
+ },
173
+ ["megaeth" /* MegaETH */]: {
174
+ id: 4326,
175
+ rpc: "https://carrot.megaeth.com",
176
+ usdt: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
177
+ decimals: 6
178
+ },
179
+ ["base" /* Base */]: {
180
+ id: 8453,
181
+ rpc: "https://mainnet.base.org",
182
+ usdt: "0x102d758f688a4C1C5a80b116bD945d4455460282",
183
+ decimals: 6
184
+ },
185
+ ["bsc" /* BSC */]: {
186
+ id: 56,
187
+ rpc: "https://bsc-rpc.publicnode.com",
188
+ usdt: "0x55d398326f99059fF775485246999027B3197955",
189
+ decimals: 18
190
+ },
191
+ ["hyperevm" /* HyperEVM */]: {
192
+ id: 999,
193
+ rpc: "https://rpc.hyperliquid.xyz/evm",
194
+ usdt: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
195
+ decimals: 6
196
+ }
197
+ };
198
+
199
+ // src/services/layerzero.ts
200
+ var C = CHAIN_CONFIGS;
201
+ var BRIDGE_CHAIN_CONFIGS = {
202
+ // ─── Testnet ───────────────────────────────────────────────────────────────
203
+ ["sepolia" /* Sepolia */]: { ...C["sepolia" /* Sepolia */], lzEid: 40161, oftAdapter: "0xc099cD946d5efCC35A99D64E808c1430cEf08126" },
204
+ ["stable-testnet" /* StableTestnet */]: { ...C["stable-testnet" /* StableTestnet */], lzEid: 40374, oftAdapter: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736" },
205
+ // ─── Mainnet ──────────────────────────────────────────────────────────────
206
+ ["stable" /* Stable */]: { ...C["stable" /* Stable */], lzEid: 30396, oftAdapter: "0xedaba024be4d87974d5aB11C6Dd586963CcCB027" },
207
+ ["ethereum" /* Ethereum */]: { ...C["ethereum" /* Ethereum */], lzEid: 30101, oftAdapter: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee" },
208
+ ["arbitrum" /* Arbitrum */]: { ...C["arbitrum" /* Arbitrum */], lzEid: 30110, oftAdapter: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92" },
209
+ ["ink" /* Ink */]: { ...C["ink" /* Ink */], lzEid: 30339, oftAdapter: "0x1cB6De532588fCA4a21B7209DE7C456AF8434A65" },
210
+ ["bera" /* Bera */]: { ...C["bera" /* Bera */], lzEid: 30362, oftAdapter: "0x3Dc96399109df5ceb2C226664A086140bD0379cB" },
211
+ ["megaeth" /* MegaETH */]: { ...C["megaeth" /* MegaETH */], lzEid: 30398, oftAdapter: "0x9151434b16b9763660705744891fa906f660ecc5" },
212
+ ["hyperevm" /* HyperEVM */]: { ...C["hyperevm" /* HyperEVM */], lzEid: 30367, oftAdapter: "0x904861a24F30EC96ea7CFC3bE9EA4B476d237e98" }
213
+ };
214
+ var OFT_ADAPTER_ABI = [
215
+ {
216
+ name: "quoteSend",
217
+ type: "function",
218
+ inputs: [
219
+ {
220
+ name: "_sendParam",
221
+ type: "tuple",
222
+ components: [
223
+ { name: "dstEid", type: "uint32" },
224
+ { name: "to", type: "bytes32" },
225
+ { name: "amountLD", type: "uint256" },
226
+ { name: "minAmountLD", type: "uint256" },
227
+ { name: "extraOptions", type: "bytes" },
228
+ { name: "composeMsg", type: "bytes" },
229
+ { name: "oftCmd", type: "bytes" }
230
+ ]
231
+ },
232
+ { name: "_payInLzToken", type: "bool" }
233
+ ],
234
+ outputs: [
235
+ {
236
+ name: "msgFee",
237
+ type: "tuple",
238
+ components: [
239
+ { name: "nativeFee", type: "uint256" },
240
+ { name: "lzTokenFee", type: "uint256" }
241
+ ]
242
+ }
243
+ ],
244
+ stateMutability: "view"
245
+ },
246
+ {
247
+ name: "send",
248
+ type: "function",
249
+ inputs: [
250
+ {
251
+ name: "_sendParam",
252
+ type: "tuple",
253
+ components: [
254
+ { name: "dstEid", type: "uint32" },
255
+ { name: "to", type: "bytes32" },
256
+ { name: "amountLD", type: "uint256" },
257
+ { name: "minAmountLD", type: "uint256" },
258
+ { name: "extraOptions", type: "bytes" },
259
+ { name: "composeMsg", type: "bytes" },
260
+ { name: "oftCmd", type: "bytes" }
261
+ ]
262
+ },
263
+ {
264
+ name: "_fee",
265
+ type: "tuple",
266
+ components: [
267
+ { name: "nativeFee", type: "uint256" },
268
+ { name: "lzTokenFee", type: "uint256" }
269
+ ]
270
+ },
271
+ { name: "_refundAddress", type: "address" }
272
+ ],
273
+ outputs: [],
274
+ stateMutability: "payable"
275
+ }
276
+ ];
277
+ function buildLzOptions(gasLimit = 0n) {
278
+ return _viem.concat.call(void 0, [
279
+ _viem.numberToHex.call(void 0, 3, { size: 2 }),
280
+ // TYPE_3 header
281
+ _viem.numberToHex.call(void 0, 1, { size: 1 }),
282
+ // Executor worker ID
283
+ _viem.numberToHex.call(void 0, 17, { size: 2 }),
284
+ // option payload length = 17 bytes
285
+ _viem.numberToHex.call(void 0, 1, { size: 1 }),
286
+ // LzReceive option type
287
+ _viem.numberToHex.call(void 0, gasLimit, { size: 16 })
288
+ // gas limit as uint128
289
+ ]);
290
+ }
291
+ var LayerZeroService = class {
292
+ constructor(config) {
293
+ this.config = config;
294
+ }
295
+ async quoteBridge({ fromChain, toChain, amount, recipient }) {
296
+ const recipientAddr = _nullishCoalesce(recipient, () => ( "0x0000000000000000000000000000000000000000"));
297
+ this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "quoteBridge");
298
+ const { src, dst } = this.resolveChains(fromChain, toChain);
299
+ const oftAdapter = src.oftAdapter;
300
+ const amountLD = _viem.parseUnits.call(void 0, String(amount), src.decimals);
301
+ try {
302
+ const { nativeFee, lzTokenFee } = await _viem.createPublicClient.call(void 0, {
303
+ chain: makeChain(src.id, src.rpc),
304
+ transport: _viem.http.call(void 0, src.rpc)
305
+ }).readContract({
306
+ address: oftAdapter,
307
+ abi: OFT_ADAPTER_ABI,
308
+ functionName: "quoteSend",
309
+ args: [this.buildSendParams(dst, amountLD, recipientAddr), false]
310
+ });
311
+ return { nativeFee, lzTokenFee, toAmount: amount };
312
+ } catch (err) {
313
+ throw new StableQuoteError({
314
+ provider: "layerzero",
315
+ shortMessage: "quoteBridge: quoteSend call failed",
316
+ cause: err
317
+ });
318
+ }
319
+ }
320
+ async bridge({ fromChain, toChain, fromToken, toToken, amount, recipient, quote }) {
321
+ const from = _nullishCoalesce(_optionalChain([this, 'access', _2 => _2.config, 'access', _3 => _3.walletClient, 'optionalAccess', _4 => _4.account, 'optionalAccess', _5 => _5.address]), () => ( _optionalChain([this, 'access', _6 => _6.config, 'access', _7 => _7.account, 'optionalAccess', _8 => _8.address])));
322
+ if (!from) {
323
+ throw new StableValidationError(
324
+ "signer",
325
+ void 0,
326
+ "bridge: requires a signer \u2014 set walletClient or account in StableConfig"
327
+ );
328
+ }
329
+ const recipientAddr = _nullishCoalesce(recipient, () => ( from));
330
+ this.validateBridgeParams({ fromChain, toChain, amount, recipient: recipientAddr }, "bridge");
331
+ const { src, dst } = this.resolveChains(fromChain, toChain);
332
+ const oftAdapter = src.oftAdapter;
333
+ const srcChain = makeChain(src.id, src.rpc);
334
+ const wc = _nullishCoalesce(this.config.walletClient, () => ( _viem.createWalletClient.call(void 0, {
335
+ account: this.config.account,
336
+ chain: srcChain,
337
+ transport: _nullishCoalesce(this.config.transport, () => ( _viem.http.call(void 0, src.rpc)))
338
+ })));
339
+ const pc = _viem.createPublicClient.call(void 0, { chain: srcChain, transport: _viem.http.call(void 0, src.rpc) });
340
+ try {
341
+ await wc.switchChain({ id: src.id });
342
+ } catch (err) {
343
+ throw new StableTransactionError({
344
+ phase: "switch_chain",
345
+ shortMessage: `bridge: wallet rejected or failed to switch to chain ${src.id}`,
346
+ chainId: src.id,
347
+ cause: err
348
+ });
349
+ }
350
+ const amountLD = _viem.parseUnits.call(void 0, String(amount), src.decimals);
351
+ const sendParams = this.buildSendParams(dst, amountLD, recipientAddr);
352
+ const lzFee = _optionalChain([quote, 'optionalAccess', _9 => _9.nativeFee]) !== void 0 && _optionalChain([quote, 'optionalAccess', _10 => _10.lzTokenFee]) !== void 0 ? { nativeFee: quote.nativeFee, lzTokenFee: quote.lzTokenFee } : await this.quoteBridge({ fromChain, toChain, fromToken, toToken, amount, recipient: recipientAddr });
353
+ const { nativeFee = 0n, lzTokenFee = 0n } = lzFee;
354
+ const allowance = await pc.readContract({
355
+ address: src.usdt,
356
+ abi: _viem.erc20Abi,
357
+ functionName: "allowance",
358
+ args: [from, oftAdapter]
359
+ });
360
+ if (allowance < amountLD) {
361
+ let approveHash;
362
+ try {
363
+ approveHash = await wc.writeContract({
364
+ address: src.usdt,
365
+ abi: _viem.erc20Abi,
366
+ functionName: "approve",
367
+ args: [oftAdapter, amountLD],
368
+ account: wc.account,
369
+ chain: srcChain
370
+ });
371
+ } catch (err) {
372
+ throw new StableTransactionError({
373
+ phase: "approve",
374
+ shortMessage: "bridge: USDT0 approve failed",
375
+ chainId: src.id,
376
+ cause: err
377
+ });
378
+ }
379
+ const approveReceipt = await pc.waitForTransactionReceipt({ hash: approveHash });
380
+ if (approveReceipt.status === "reverted") {
381
+ throw new StableTransactionError({
382
+ phase: "approve",
383
+ shortMessage: "bridge: USDT0 approve reverted on-chain",
384
+ txHash: approveReceipt.transactionHash,
385
+ chainId: src.id
386
+ });
387
+ }
388
+ }
389
+ try {
390
+ const hash = await wc.writeContract({
391
+ address: oftAdapter,
392
+ abi: OFT_ADAPTER_ABI,
393
+ functionName: "send",
394
+ args: [sendParams, { nativeFee, lzTokenFee }, from],
395
+ value: nativeFee,
396
+ account: wc.account,
397
+ chain: srcChain
398
+ });
399
+ return { txHash: hash, toAmount: amount };
400
+ } catch (err) {
401
+ throw new StableTransactionError({
402
+ phase: "send",
403
+ shortMessage: "bridge: OFT send failed",
404
+ chainId: src.id,
405
+ cause: err
406
+ });
407
+ }
408
+ }
409
+ validateBridgeParams(params, method) {
410
+ if (!_viem.isAddress.call(void 0, params.recipient)) {
411
+ throw new StableValidationError(
412
+ "recipient",
413
+ params.recipient,
414
+ `${method}: invalid recipient address "${params.recipient}"`
415
+ );
416
+ }
417
+ if (!Number.isFinite(params.amount) || params.amount <= 0) {
418
+ throw new StableValidationError(
419
+ "amount",
420
+ params.amount,
421
+ `${method}: amount must be a finite positive number`
422
+ );
423
+ }
424
+ if (params.fromChain === params.toChain) {
425
+ throw new StableValidationError(
426
+ "toChain",
427
+ params.toChain,
428
+ `${method}: fromChain and toChain must differ`
429
+ );
430
+ }
431
+ }
432
+ resolveChains(fromChain, toChain) {
433
+ const src = BRIDGE_CHAIN_CONFIGS[fromChain];
434
+ const dst = BRIDGE_CHAIN_CONFIGS[toChain];
435
+ if (!src) throw new StableValidationError("fromChain", fromChain, `Unsupported source chain: "${fromChain}"`);
436
+ if (!dst) throw new StableValidationError("toChain", toChain, `Unsupported destination chain: "${toChain}"`);
437
+ if (!src.oftAdapter) {
438
+ throw new StableValidationError("fromChain", fromChain, `No OFT adapter configured for "${fromChain}"`);
439
+ }
440
+ return { src, dst };
441
+ }
442
+ buildSendParams(dst, amountLD, recipient) {
443
+ return {
444
+ dstEid: dst.lzEid,
445
+ to: _viem.padHex.call(void 0, recipient, { dir: "left", size: 32 }),
446
+ amountLD,
447
+ minAmountLD: amountLD,
448
+ extraOptions: buildLzOptions(),
449
+ composeMsg: "0x",
450
+ oftCmd: "0x"
451
+ };
452
+ }
453
+ };
454
+
455
+ // src/services/lifi.ts
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+ // src/helpers/retry.ts
467
+ var HttpError = class extends Error {
468
+ constructor(message, status, retryable) {
469
+ super(message);
470
+ this.status = status;
471
+ this.retryable = retryable;
472
+ this.name = "HttpError";
473
+ }
474
+ };
475
+ function isRetryableError(err) {
476
+ if (err instanceof HttpError) return err.retryable;
477
+ return err instanceof TypeError;
478
+ }
479
+ async function retryWithBackoff(fn, isRetryable = isRetryableError, opts = {}) {
480
+ const maxAttempts = _nullishCoalesce(opts.maxAttempts, () => ( 3));
481
+ const base = _nullishCoalesce(opts.baseDelayMs, () => ( 250));
482
+ const max = _nullishCoalesce(opts.maxDelayMs, () => ( 4e3));
483
+ let lastErr;
484
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
485
+ try {
486
+ return await fn();
487
+ } catch (err) {
488
+ lastErr = err;
489
+ if (attempt === maxAttempts - 1 || !isRetryable(err)) throw err;
490
+ const delay = Math.min(max, base * 2 ** attempt);
491
+ const jittered = delay * (0.75 + Math.random() * 0.5);
492
+ await new Promise((r) => setTimeout(r, jittered));
493
+ }
494
+ }
495
+ throw lastErr;
496
+ }
497
+
498
+ // src/services/lifi.ts
499
+ var LiFiService = class {
500
+ constructor(config) {
501
+ this.config = config;
502
+ }
503
+ async quoteSwap(params) {
504
+ this._validateSwapParams(params);
505
+ const { fromToken, toToken, amount, fromDecimals = 6, toAddress } = params;
506
+ const from = this._requireFrom();
507
+ const chain = this.config.chainId.toString();
508
+ const fromAmountRaw = _viem.parseUnits.call(void 0, String(amount), fromDecimals).toString();
509
+ const qs = new URLSearchParams({
510
+ fromChain: chain,
511
+ toChain: chain,
512
+ fromToken,
513
+ toToken,
514
+ fromAmount: fromAmountRaw,
515
+ fromAddress: from,
516
+ ...toAddress && { toAddress }
517
+ });
518
+ const quote = await this._fetchQuote(qs);
519
+ return this._toSwapQuote(quote);
520
+ }
521
+ async swap(params) {
522
+ this._validateSwapParams(params);
523
+ const quote = await _asyncNullishCoalesce(params.quote, async () => ( await this.quoteSwap(params)));
524
+ return this._executeSwapQuote(quote, this.config.rpc, this.config.chainId);
525
+ }
526
+ async quoteBridge(params) {
527
+ this._validateBridgeParams(params);
528
+ const { fromChain, toChain, fromToken, toToken, amount, fromDecimals = 6, recipient } = params;
529
+ const from = _nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _11 => _11.config, 'access', _12 => _12.walletClient, 'optionalAccess', _13 => _13.account, 'optionalAccess', _14 => _14.address]), () => ( _optionalChain([this, 'access', _15 => _15.config, 'access', _16 => _16.account, 'optionalAccess', _17 => _17.address]))), () => ( "0x0000000000000000000000000000000000000000"));
530
+ const src = CHAIN_CONFIGS[fromChain];
531
+ const dst = CHAIN_CONFIGS[toChain];
532
+ if (!src) throw new StableValidationError("fromChain", fromChain, `LI.FI bridge: unsupported source chain "${fromChain}"`);
533
+ if (!dst) throw new StableValidationError("toChain", toChain, `LI.FI bridge: unsupported destination chain "${toChain}"`);
534
+ const qs = new URLSearchParams({
535
+ fromChain: src.id.toString(),
536
+ toChain: dst.id.toString(),
537
+ fromToken,
538
+ toToken,
539
+ fromAmount: _viem.parseUnits.call(void 0, String(amount), fromDecimals).toString(),
540
+ fromAddress: from,
541
+ ...recipient && { toAddress: recipient }
542
+ });
543
+ const quote = this._toSwapQuote(await this._fetchQuote(qs));
544
+ return { toAmount: quote.toAmount };
545
+ }
546
+ async bridge(params) {
547
+ this._validateBridgeParams(params);
548
+ const { fromChain, toChain, fromToken, toToken, amount, fromDecimals = 6, recipient } = params;
549
+ const from = this._requireFrom();
550
+ const src = CHAIN_CONFIGS[fromChain];
551
+ const dst = CHAIN_CONFIGS[toChain];
552
+ if (!src) {
553
+ throw new StableValidationError("fromChain", fromChain, `LI.FI bridge: unsupported source chain "${fromChain}"`);
554
+ }
555
+ if (!dst) {
556
+ throw new StableValidationError("toChain", toChain, `LI.FI bridge: unsupported destination chain "${toChain}"`);
557
+ }
558
+ const fromAmountRaw = _viem.parseUnits.call(void 0, String(amount), fromDecimals).toString();
559
+ const qs = new URLSearchParams({
560
+ fromChain: src.id.toString(),
561
+ toChain: dst.id.toString(),
562
+ fromToken,
563
+ toToken,
564
+ fromAmount: fromAmountRaw,
565
+ fromAddress: from,
566
+ ...recipient && { toAddress: recipient }
567
+ });
568
+ const quote = this._toSwapQuote(await this._fetchQuote(qs));
569
+ return this._executeSwapQuote(quote, src.rpc, src.id);
570
+ }
571
+ _validateSwapParams({ fromToken, toToken, amount, toAddress }) {
572
+ if (!_viem.isAddress.call(void 0, fromToken)) {
573
+ throw new StableValidationError("fromToken", fromToken, `swap: invalid fromToken address "${fromToken}"`);
574
+ }
575
+ if (!_viem.isAddress.call(void 0, toToken)) {
576
+ throw new StableValidationError("toToken", toToken, `swap: invalid toToken address "${toToken}"`);
577
+ }
578
+ if (fromToken.toLowerCase() === toToken.toLowerCase()) {
579
+ throw new StableValidationError("toToken", toToken, "swap: fromToken and toToken must differ");
580
+ }
581
+ if (!Number.isFinite(amount) || amount <= 0) {
582
+ throw new StableValidationError("amount", amount, "swap: amount must be a finite positive number");
583
+ }
584
+ if (toAddress !== void 0 && !_viem.isAddress.call(void 0, toAddress)) {
585
+ throw new StableValidationError("toAddress", toAddress, `swap: invalid toAddress "${toAddress}"`);
586
+ }
587
+ }
588
+ _validateBridgeParams({ fromChain, toChain, fromToken, toToken, amount, recipient }) {
589
+ if (!_viem.isAddress.call(void 0, fromToken)) {
590
+ throw new StableValidationError("fromToken", fromToken, `LI.FI bridge: invalid fromToken address "${fromToken}"`);
591
+ }
592
+ if (!_viem.isAddress.call(void 0, toToken)) {
593
+ throw new StableValidationError("toToken", toToken, `LI.FI bridge: invalid toToken address "${toToken}"`);
594
+ }
595
+ if (!Number.isFinite(amount) || amount <= 0) {
596
+ throw new StableValidationError("amount", amount, "LI.FI bridge: amount must be a finite positive number");
597
+ }
598
+ if (recipient !== void 0 && !_viem.isAddress.call(void 0, recipient)) {
599
+ throw new StableValidationError("recipient", recipient, `LI.FI bridge: invalid recipient address "${recipient}"`);
600
+ }
601
+ if (fromChain === toChain) {
602
+ throw new StableValidationError("toChain", toChain, "LI.FI bridge: fromChain and toChain must differ");
603
+ }
604
+ }
605
+ _requireFrom() {
606
+ const from = _nullishCoalesce(_optionalChain([this, 'access', _18 => _18.config, 'access', _19 => _19.walletClient, 'optionalAccess', _20 => _20.account, 'optionalAccess', _21 => _21.address]), () => ( _optionalChain([this, 'access', _22 => _22.config, 'access', _23 => _23.account, 'optionalAccess', _24 => _24.address])));
607
+ if (!from) {
608
+ throw new StableValidationError(
609
+ "signer",
610
+ void 0,
611
+ "LI.FI: requires a signer \u2014 set walletClient or account in StableConfig"
612
+ );
613
+ }
614
+ return from;
615
+ }
616
+ async _fetchQuote(qs) {
617
+ const url = `https://li.quest/v1/quote?${qs}`;
618
+ try {
619
+ return await retryWithBackoff(() => this._fetchQuoteOnce(url));
620
+ } catch (err) {
621
+ if (err instanceof StableQuoteError) throw err;
622
+ if (err instanceof HttpError) {
623
+ throw new StableQuoteError({
624
+ provider: "lifi",
625
+ shortMessage: err.message,
626
+ httpStatus: err.status,
627
+ cause: err
628
+ });
629
+ }
630
+ throw new StableNetworkError({
631
+ shortMessage: "LI.FI: quote request failed before reaching the server",
632
+ url,
633
+ cause: err
634
+ });
635
+ }
636
+ }
637
+ async _fetchQuoteOnce(url) {
638
+ const res = await fetch(url);
639
+ const body = await res.json().catch(() => void 0);
640
+ if (!res.ok) {
641
+ const providerMessage = isObject(body) && typeof body.message === "string" ? body.message : void 0;
642
+ const providerCode = isObject(body) && typeof body.code === "string" ? body.code : void 0;
643
+ if (res.status === 429 || res.status >= 500) {
644
+ throw new HttpError(`LI.FI: ${_nullishCoalesce(providerMessage, () => ( res.statusText))}`, res.status, true);
645
+ }
646
+ throw new StableQuoteError({
647
+ provider: "lifi",
648
+ shortMessage: `LI.FI: ${_nullishCoalesce(providerMessage, () => ( res.statusText))}`,
649
+ httpStatus: res.status,
650
+ providerCode,
651
+ body
652
+ });
653
+ }
654
+ if (!isLiFiQuoteShape(body)) {
655
+ throw new StableQuoteError({
656
+ provider: "lifi",
657
+ shortMessage: "LI.FI: unexpected quote response shape",
658
+ httpStatus: res.status,
659
+ body
660
+ });
661
+ }
662
+ return body;
663
+ }
664
+ _toSwapQuote(quote) {
665
+ return {
666
+ toAmount: Number(_viem.formatUnits.call(void 0, BigInt(quote.estimate.toAmount), quote.action.toToken.decimals)),
667
+ fromAmount: BigInt(quote.estimate.fromAmount),
668
+ fromToken: quote.action.fromToken.address,
669
+ approvalAddress: quote.estimate.approvalAddress,
670
+ transactionRequest: quote.transactionRequest
671
+ };
672
+ }
673
+ async _executeSwapQuote(quote, srcRpc, expectedChainId) {
674
+ const from = this._requireFrom();
675
+ const { transactionRequest } = quote;
676
+ const srcChainId = transactionRequest.chainId;
677
+ if (srcChainId !== expectedChainId) {
678
+ throw new StableQuoteError({
679
+ provider: "lifi",
680
+ shortMessage: `LI.FI: returned tx for chain ${srcChainId} but expected ${expectedChainId}`,
681
+ providerCode: "chain_mismatch",
682
+ body: quote
683
+ });
684
+ }
685
+ const srcChain = makeChain(srcChainId, srcRpc);
686
+ const wc = _nullishCoalesce(this.config.walletClient, () => ( _viem.createWalletClient.call(void 0, {
687
+ account: this.config.account,
688
+ chain: srcChain,
689
+ transport: _nullishCoalesce(this.config.transport, () => ( _viem.http.call(void 0, srcRpc)))
690
+ })));
691
+ try {
692
+ await wc.switchChain({ id: srcChainId });
693
+ } catch (err) {
694
+ throw new StableTransactionError({
695
+ phase: "switch_chain",
696
+ shortMessage: `LI.FI: wallet rejected or failed to switch to chain ${srcChainId}`,
697
+ chainId: srcChainId,
698
+ cause: err
699
+ });
700
+ }
701
+ await this._ensureApproval(wc, srcChain, srcRpc, srcChainId, from, quote);
702
+ let hash;
703
+ try {
704
+ hash = await wc.sendTransaction({
705
+ to: transactionRequest.to,
706
+ data: transactionRequest.data,
707
+ value: transactionRequest.value && transactionRequest.value !== "0x" ? BigInt(transactionRequest.value) : 0n,
708
+ gas: transactionRequest.gasLimit ? BigInt(transactionRequest.gasLimit) : void 0,
709
+ account: wc.account,
710
+ chain: srcChain
711
+ });
712
+ } catch (err) {
713
+ throw new StableTransactionError({
714
+ phase: "send",
715
+ shortMessage: "LI.FI: swap/bridge transaction failed",
716
+ chainId: srcChainId,
717
+ cause: err
718
+ });
719
+ }
720
+ return { txHash: hash, toAmount: quote.toAmount };
721
+ }
722
+ async _ensureApproval(wc, srcChain, srcRpc, srcChainId, from, quote) {
723
+ const tokenAddr = quote.fromToken;
724
+ const isNative = tokenAddr.toLowerCase() === "0x0000000000000000000000000000000000000000" || tokenAddr.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
725
+ if (isNative || !quote.approvalAddress) return;
726
+ const pc = _viem.createPublicClient.call(void 0, { chain: srcChain, transport: _viem.http.call(void 0, srcRpc) });
727
+ const allowance = await pc.readContract({
728
+ address: tokenAddr,
729
+ abi: _viem.erc20Abi,
730
+ functionName: "allowance",
731
+ args: [from, quote.approvalAddress]
732
+ });
733
+ if (allowance >= quote.fromAmount) return;
734
+ let approveHash;
735
+ try {
736
+ approveHash = await wc.writeContract({
737
+ address: tokenAddr,
738
+ abi: _viem.erc20Abi,
739
+ functionName: "approve",
740
+ args: [quote.approvalAddress, quote.fromAmount],
741
+ account: wc.account,
742
+ chain: srcChain
743
+ });
744
+ } catch (err) {
745
+ throw new StableTransactionError({
746
+ phase: "approve",
747
+ shortMessage: "LI.FI: token approve failed",
748
+ chainId: srcChainId,
749
+ cause: err
750
+ });
751
+ }
752
+ const approveReceipt = await pc.waitForTransactionReceipt({ hash: approveHash });
753
+ if (approveReceipt.status === "reverted") {
754
+ throw new StableTransactionError({
755
+ phase: "approve",
756
+ shortMessage: "LI.FI: token approve reverted on-chain",
757
+ txHash: approveReceipt.transactionHash,
758
+ chainId: srcChainId
759
+ });
760
+ }
761
+ }
762
+ };
763
+ function isObject(v) {
764
+ return typeof v === "object" && v !== null;
765
+ }
766
+ function isLiFiQuoteShape(v) {
767
+ if (!isObject(v)) return false;
768
+ const tx = v.transactionRequest;
769
+ const est = v.estimate;
770
+ const act = v.action;
771
+ return isObject(tx) && typeof tx.to === "string" && typeof tx.data === "string" && typeof tx.chainId === "number" && isObject(est) && typeof est.fromAmount === "string" && typeof est.toAmount === "string" && isObject(act) && isObject(act.fromToken) && isObject(act.toToken);
772
+ }
773
+
774
+ // src/stable.ts
775
+ function isUsdt0Route(fromChain, toChain, fromToken, toToken) {
776
+ const src = BRIDGE_CHAIN_CONFIGS[fromChain];
777
+ const dst = BRIDGE_CHAIN_CONFIGS[toChain];
778
+ return !!src && !!dst && src.lzOutbound !== false && src.usdt.toLowerCase() === fromToken.toLowerCase() && dst.usdt.toLowerCase() === toToken.toLowerCase();
779
+ }
780
+ function validateTransferParams({ to, amount, token, tokenDecimals }) {
781
+ if (!_viem.isAddress.call(void 0, to)) {
782
+ throw new StableValidationError("to", to, `transfer: invalid recipient address "${to}"`);
783
+ }
784
+ if (!Number.isFinite(amount) || amount <= 0) {
785
+ throw new StableValidationError("amount", amount, "transfer: amount must be a finite positive number");
786
+ }
787
+ if (token !== void 0 && !_viem.isAddress.call(void 0, token)) {
788
+ throw new StableValidationError("token", token, `transfer: invalid token address "${token}"`);
789
+ }
790
+ if (tokenDecimals !== void 0 && (!Number.isInteger(tokenDecimals) || tokenDecimals < 0 || tokenDecimals > 77)) {
791
+ throw new StableValidationError("tokenDecimals", tokenDecimals, "transfer: tokenDecimals must be an integer between 0 and 77");
792
+ }
793
+ }
794
+ async function resolveTokenDecimals(publicClient, token, tokenDecimals) {
795
+ if (tokenDecimals !== void 0) return tokenDecimals;
796
+ try {
797
+ return await publicClient.readContract({ address: token, abi: _viem.erc20Abi, functionName: "decimals" });
798
+ } catch (e2) {
799
+ throw new StableValidationError(
800
+ "tokenDecimals",
801
+ void 0,
802
+ `transfer: could not fetch decimals for token "${token}" \u2014 pass tokenDecimals explicitly`
803
+ );
804
+ }
805
+ }
806
+ async function executeTokenTransfer(wc, publicClient, token, to, amount, tokenDecimals, acc, chain) {
807
+ const code = await publicClient.getCode({ address: token });
808
+ if (!code || code === "0x") {
809
+ throw new StableValidationError(
810
+ "token",
811
+ token,
812
+ `transfer: no contract found at "${token}" on chain ${chain.id} \u2014 check the address and network`
813
+ );
814
+ }
815
+ const decimals = await resolveTokenDecimals(publicClient, token, tokenDecimals);
816
+ return wc.writeContract({
817
+ address: token,
818
+ abi: _viem.erc20Abi,
819
+ functionName: "transfer",
820
+ args: [to, _viem.parseUnits.call(void 0, String(amount), decimals)],
821
+ account: acc,
822
+ chain
823
+ });
824
+ }
825
+ function createStable({
826
+ network = "mainnet" /* Mainnet */,
827
+ rpc: rpcOverride,
828
+ account,
829
+ transport,
830
+ walletClient
831
+ }) {
832
+ const chain = network === "mainnet" /* Mainnet */ ? _chains.stable : _chains.stableTestnet;
833
+ const rpc = (_nullishCoalesce(rpcOverride, () => ( chain.rpcUrls.default.http[0]))).replace(/\/$/, "");
834
+ const signerConfig = { account, transport, walletClient };
835
+ const lz = new LayerZeroService(signerConfig);
836
+ const lifi = new LiFiService({ ...signerConfig, rpc, chainId: chain.id });
837
+ async function transfer(params) {
838
+ validateTransferParams(params);
839
+ const { from, to, amount, token, tokenDecimals } = params;
840
+ const acc = _nullishCoalesce(_nullishCoalesce(_optionalChain([walletClient, 'optionalAccess', _25 => _25.account]), () => ( account)), () => ( from));
841
+ const wc = _nullishCoalesce(walletClient, () => ( _viem.createWalletClient.call(void 0, {
842
+ account: acc,
843
+ chain,
844
+ transport: _nullishCoalesce(transport, () => ( _viem.http.call(void 0, rpc)))
845
+ })));
846
+ try {
847
+ await wc.switchChain({ id: chain.id });
848
+ } catch (err) {
849
+ throw new StableTransactionError({
850
+ phase: "switch_chain",
851
+ shortMessage: `transfer: wallet rejected or failed to switch to chain ${chain.id}`,
852
+ chainId: chain.id,
853
+ cause: err
854
+ });
855
+ }
856
+ const publicClient = _viem.createPublicClient.call(void 0, { chain, transport: _viem.http.call(void 0, rpc) });
857
+ const hash = token !== void 0 ? await executeTokenTransfer(wc, publicClient, token, to, amount, tokenDecimals, acc, chain) : await wc.sendTransaction({ account: acc, chain, to, value: _viem.parseUnits.call(void 0, String(amount), 18) });
858
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
859
+ if (receipt.status === "reverted") {
860
+ throw new StableTransactionError({
861
+ phase: "receipt",
862
+ shortMessage: "transfer: transaction reverted on-chain",
863
+ txHash: receipt.transactionHash,
864
+ chainId: chain.id
865
+ });
866
+ }
867
+ return { txHash: receipt.transactionHash };
868
+ }
869
+ function quoteBridge(params) {
870
+ return isUsdt0Route(params.fromChain, params.toChain, params.fromToken, params.toToken) ? lz.quoteBridge(params) : lifi.quoteBridge(params);
871
+ }
872
+ function bridge(params) {
873
+ return isUsdt0Route(params.fromChain, params.toChain, params.fromToken, params.toToken) ? lz.bridge(params) : lifi.bridge(params);
874
+ }
875
+ return {
876
+ transfer,
877
+ quoteBridge,
878
+ bridge,
879
+ quoteSwap: (params) => lifi.quoteSwap(params),
880
+ swap: (params) => lifi.swap(params)
881
+ };
882
+ }
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+
891
+
892
+
893
+ exports.CHAIN_CONFIGS = CHAIN_CONFIGS; exports.Chain = Chain; exports.Network = Network; exports.StableError = StableError; exports.StableNetworkError = StableNetworkError; exports.StableQuoteError = StableQuoteError; exports.StableTransactionError = StableTransactionError; exports.StableValidationError = StableValidationError; exports.createStable = createStable;