@t2000/sdk 4.4.0 → 5.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @t2000/sdk
2
2
 
3
- The TypeScript SDK for Agent Wallets on Sui. One class (`T2000`) — wallet signing, gasless USDC + USDsui sends, Cetus swap routing, MPP paid API access, and programmatic NAVI lending builders.
3
+ The TypeScript SDK for Agent Wallets on Sui. One class (`T2000`) — wallet signing, gasless USDC + USDsui sends, Cetus swap routing, and x402 paid-API access (pay any API in USDC, no keys).
4
4
 
5
5
  [![npm @t2000/sdk](https://img.shields.io/npm/v/@t2000/sdk?label=%40t2000%2Fsdk)](https://www.npmjs.com/package/@t2000/sdk)
6
6
  [![npm @t2000/cli](https://img.shields.io/npm/v/@t2000/cli?label=%40t2000%2Fcli)](https://www.npmjs.com/package/@t2000/cli)
@@ -29,11 +29,11 @@ await agent.swap({ from: 'USDC', to: 'SUI', amount: 100 }); // Cetus, ne
29
29
  await agent.pay({ url: 'https://mpp.t2000.ai/openai/v1/chat/completions', method: 'POST', body, maxPrice: 0.10 });
30
30
  ```
31
31
 
32
- USDC + USDsui sends and MPP USDC payments are gasless (Sui foundation's `0x2::balance::send_funds` sponsor). SUI sends and Cetus swaps need gas — keep ~0.05 SUI on hand.
32
+ USDC + USDsui sends and x402 USDC payments are gasless (Sui foundation's `0x2::balance::send_funds` sponsor). SUI sends and Cetus swaps need gas — keep ~0.05 SUI on hand.
33
33
 
34
34
  ## Full reference
35
35
 
36
- Factory methods, full API surface, supported assets, NAVI lending builders, error handling, architecture →
36
+ Factory methods, full API surface, supported assets, Cetus swap routing, x402 payments, error handling, architecture →
37
37
  **[developers.t2000.ai/agent-sdk](https://developers.t2000.ai/agent-sdk)**
38
38
 
39
39
  ## License
package/dist/browser.cjs CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
- require('@mysten/sui/jsonRpc');
4
- require('@mysten/sui/grpc');
3
+ var transactions = require('@mysten/sui/transactions');
5
4
  var utils = require('@mysten/sui/utils');
5
+ require('@mysten/sui/grpc');
6
+ require('@mysten/sui/graphql');
6
7
  var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
7
8
  require('bn.js');
8
9
 
@@ -106,20 +107,33 @@ var init_errors = __esm({
106
107
  });
107
108
 
108
109
  // src/token-registry.ts
109
- function isTier1(coinType) {
110
- const meta = BY_TYPE.get(coinType);
111
- return meta?.tier === 1;
112
- }
113
- function isTier2(coinType) {
114
- const meta = BY_TYPE.get(coinType);
115
- return meta?.tier === 2;
116
- }
117
- function isSupported(coinType) {
118
- const meta = BY_TYPE.get(coinType);
119
- return meta?.tier !== void 0;
110
+ var token_registry_exports = {};
111
+ __export(token_registry_exports, {
112
+ COIN_REGISTRY: () => exports.COIN_REGISTRY,
113
+ ETH_TYPE: () => exports.ETH_TYPE,
114
+ IKA_TYPE: () => exports.IKA_TYPE,
115
+ LOFI_TYPE: () => exports.LOFI_TYPE,
116
+ MANIFEST_TYPE: () => exports.MANIFEST_TYPE,
117
+ NAVX_TYPE: () => exports.NAVX_TYPE,
118
+ SUI_TYPE: () => exports.SUI_TYPE,
119
+ TOKEN_MAP: () => exports.TOKEN_MAP,
120
+ USDC_TYPE: () => exports.USDC_TYPE,
121
+ USDE_TYPE: () => exports.USDE_TYPE,
122
+ USDSUI_TYPE: () => exports.USDSUI_TYPE,
123
+ USDT_TYPE: () => exports.USDT_TYPE,
124
+ WAL_TYPE: () => exports.WAL_TYPE,
125
+ WBTC_TYPE: () => exports.WBTC_TYPE,
126
+ getCoinMeta: () => getCoinMeta,
127
+ getDecimalsForCoinType: () => getDecimalsForCoinType,
128
+ isInRegistry: () => isInRegistry,
129
+ resolveSymbol: () => resolveSymbol,
130
+ resolveTokenType: () => resolveTokenType
131
+ });
132
+ function getCoinMeta(coinType) {
133
+ return BY_TYPE.get(coinType);
120
134
  }
121
- function getTier(coinType) {
122
- return BY_TYPE.get(coinType)?.tier;
135
+ function isInRegistry(coinType) {
136
+ return BY_TYPE.has(coinType);
123
137
  }
124
138
  function getDecimalsForCoinType(coinType) {
125
139
  const direct = BY_TYPE.get(coinType);
@@ -153,25 +167,25 @@ exports.COIN_REGISTRY = void 0; var BY_TYPE; exports.TOKEN_MAP = void 0; exports
153
167
  var init_token_registry = __esm({
154
168
  "src/token-registry.ts"() {
155
169
  exports.COIN_REGISTRY = {
156
- // ── Tier 1 — Financial layer ──────────────────────────────────────────
157
- USDC: { type: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC", decimals: 6, symbol: "USDC", tier: 1 },
158
- // ── Tier 2Swap assets (15 tokens) ──────────────────────────────────
159
- SUI: { type: "0x2::sui::SUI", decimals: 9, symbol: "SUI", tier: 2 },
160
- wBTC: { type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC", decimals: 8, symbol: "wBTC", tier: 2 },
161
- ETH: { type: "0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH", decimals: 8, symbol: "ETH", tier: 2 },
162
- GOLD: { type: "0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM", decimals: 9, symbol: "GOLD", tier: 2 },
163
- DEEP: { type: "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP", decimals: 6, symbol: "DEEP", tier: 2 },
164
- WAL: { type: "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL", decimals: 9, symbol: "WAL", tier: 2 },
165
- NS: { type: "0x5145494a5f5100e645e4b0aa950fa6b68f614e8c59e17bc5ded3495123a79178::ns::NS", decimals: 6, symbol: "NS", tier: 2 },
166
- IKA: { type: "0x7262fb2f7a3a14c888c438a3cd9b912469a58cf60f367352c46584262e8299aa::ika::IKA", decimals: 9, symbol: "IKA", tier: 2 },
167
- CETUS: { type: "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS", decimals: 9, symbol: "CETUS", tier: 2 },
168
- NAVX: { type: "0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX", decimals: 9, symbol: "NAVX", tier: 2 },
169
- vSUI: { type: "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT", decimals: 9, symbol: "vSUI", tier: 2 },
170
- haSUI: { type: "0xbde4ba4c2e274a60ce15c1cfff9e5c42e41654ac8b6d906a57efa4bd3c29f47d::hasui::HASUI", decimals: 9, symbol: "haSUI", tier: 2 },
171
- afSUI: { type: "0xf325ce1300e8dac124071d3152c5c5ee6174914f8bc2161e88329cf579246efc::afsui::AFSUI", decimals: 9, symbol: "afSUI", tier: 2 },
172
- LOFI: { type: "0xf22da9a24ad027cccb5f2d496cbe91de953d363513db08a3a734d361c7c17503::LOFI::LOFI", decimals: 9, symbol: "LOFI", tier: 2 },
173
- MANIFEST: { type: "0xc466c28d87b3d5cd34f3d5c088751532d71a38d93a8aae4551dd56272cfb4355::manifest::MANIFEST", decimals: 9, symbol: "MANIFEST", tier: 2 },
174
- // ── Legacy no tier, kept for display accuracy on existing positions ──
170
+ // ── Settlement stable ─────────────────────────────────────────────────
171
+ USDC: { type: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC", decimals: 6, symbol: "USDC" },
172
+ // ── Common swap assets (symbol ergonomics any coin type still swaps)
173
+ SUI: { type: "0x2::sui::SUI", decimals: 9, symbol: "SUI" },
174
+ wBTC: { type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC", decimals: 8, symbol: "wBTC" },
175
+ ETH: { type: "0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH", decimals: 8, symbol: "ETH" },
176
+ GOLD: { type: "0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM", decimals: 9, symbol: "GOLD" },
177
+ DEEP: { type: "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP", decimals: 6, symbol: "DEEP" },
178
+ WAL: { type: "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL", decimals: 9, symbol: "WAL" },
179
+ NS: { type: "0x5145494a5f5100e645e4b0aa950fa6b68f614e8c59e17bc5ded3495123a79178::ns::NS", decimals: 6, symbol: "NS" },
180
+ IKA: { type: "0x7262fb2f7a3a14c888c438a3cd9b912469a58cf60f367352c46584262e8299aa::ika::IKA", decimals: 9, symbol: "IKA" },
181
+ CETUS: { type: "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS", decimals: 9, symbol: "CETUS" },
182
+ NAVX: { type: "0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX", decimals: 9, symbol: "NAVX" },
183
+ vSUI: { type: "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT", decimals: 9, symbol: "vSUI" },
184
+ haSUI: { type: "0xbde4ba4c2e274a60ce15c1cfff9e5c42e41654ac8b6d906a57efa4bd3c29f47d::hasui::HASUI", decimals: 9, symbol: "haSUI" },
185
+ afSUI: { type: "0xf325ce1300e8dac124071d3152c5c5ee6174914f8bc2161e88329cf579246efc::afsui::AFSUI", decimals: 9, symbol: "afSUI" },
186
+ LOFI: { type: "0xf22da9a24ad027cccb5f2d496cbe91de953d363513db08a3a734d361c7c17503::LOFI::LOFI", decimals: 9, symbol: "LOFI" },
187
+ MANIFEST: { type: "0xc466c28d87b3d5cd34f3d5c088751532d71a38d93a8aae4551dd56272cfb4355::manifest::MANIFEST", decimals: 9, symbol: "MANIFEST" },
188
+ // ── Other stables (display / classification) ──────────────────────────
175
189
  USDT: { type: "0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT", decimals: 6, symbol: "USDT" },
176
190
  USDe: { type: "0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE", decimals: 6, symbol: "USDe" },
177
191
  USDSUI: { type: "0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI", decimals: 6, symbol: "USDsui" }
@@ -203,6 +217,136 @@ var init_token_registry = __esm({
203
217
  }
204
218
  });
205
219
 
220
+ // src/wallet/coinSelection.ts
221
+ var coinSelection_exports = {};
222
+ __export(coinSelection_exports, {
223
+ fetchAllCoins: () => fetchAllCoins,
224
+ selectAndSplitCoin: () => selectAndSplitCoin,
225
+ selectSuiCoin: () => selectSuiCoin
226
+ });
227
+ async function fetchAllCoins(client, owner, coinType) {
228
+ const [balance, ids] = await Promise.all([
229
+ client.core.getBalance({ owner, coinType }),
230
+ (async () => {
231
+ const out = [];
232
+ let cursor;
233
+ let hasNext = true;
234
+ while (hasNext) {
235
+ const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? void 0 });
236
+ for (const c of page.objects) out.push(c.objectId);
237
+ cursor = page.cursor;
238
+ hasNext = page.hasNextPage;
239
+ }
240
+ return out;
241
+ })()
242
+ ]);
243
+ return { ids, totalBalance: BigInt(balance.balance.balance) };
244
+ }
245
+ async function selectAndSplitCoin(tx, client, owner, coinType, amount, options = {}) {
246
+ if (options.sponsoredContext) {
247
+ return selectCoinObjectsOnly(
248
+ tx,
249
+ client,
250
+ owner,
251
+ coinType,
252
+ amount,
253
+ options.allowSwapAll ?? true,
254
+ options.mergeCache
255
+ );
256
+ }
257
+ const balanceResp = await client.core.getBalance({ owner, coinType });
258
+ const totalBalance = BigInt(balanceResp.balance.balance);
259
+ if (totalBalance === 0n) {
260
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No balance found for ${coinType}`);
261
+ }
262
+ const allowSwapAll = options.allowSwapAll ?? true;
263
+ if (amount !== "all" && amount > totalBalance && !allowSwapAll) {
264
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
265
+ available: totalBalance.toString(),
266
+ required: amount.toString()
267
+ });
268
+ }
269
+ const requested = amount === "all" ? totalBalance : amount;
270
+ const swapAll = amount === "all" || requested >= totalBalance;
271
+ const effectiveAmount = swapAll ? totalBalance : requested;
272
+ const coin = transactions.coinWithBalance({ type: coinType, balance: effectiveAmount })(tx);
273
+ return { coin, effectiveAmount, swapAll };
274
+ }
275
+ async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowSwapAll, mergeCache) {
276
+ const cached = mergeCache?.get(coinType);
277
+ if (cached) {
278
+ const requested2 = amount === "all" ? cached.remaining : amount;
279
+ if (cached.remaining === 0n || requested2 > cached.remaining) {
280
+ throw new exports.T2000Error(
281
+ "ADDRESS_BALANCE_UNSPONSORABLE",
282
+ `Not enough ${coinType} in coin objects to cover all legs of this sponsored bundle. The remaining funds are in your address balance, which sponsored transactions can't access yet.`,
283
+ { remaining: cached.remaining.toString(), requested: requested2.toString(), coinType }
284
+ );
285
+ }
286
+ const swapAll2 = amount === "all" || requested2 >= cached.remaining;
287
+ const effectiveAmount2 = swapAll2 ? cached.remaining : requested2;
288
+ const coin2 = swapAll2 ? cached.primary : tx.splitCoins(cached.primary, [effectiveAmount2])[0];
289
+ cached.remaining -= effectiveAmount2;
290
+ return { coin: coin2, effectiveAmount: effectiveAmount2, swapAll: swapAll2 };
291
+ }
292
+ const objects = [];
293
+ let coinObjectTotal = 0n;
294
+ let cursor;
295
+ let hasNext = true;
296
+ while (hasNext) {
297
+ const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? void 0 });
298
+ for (const c of page.objects) {
299
+ objects.push({ objectId: c.objectId, balance: BigInt(c.balance) });
300
+ coinObjectTotal += BigInt(c.balance);
301
+ }
302
+ cursor = page.cursor;
303
+ hasNext = page.hasNextPage;
304
+ }
305
+ const unsponsorable = () => new exports.T2000Error(
306
+ "ADDRESS_BALANCE_UNSPONSORABLE",
307
+ `These funds are in your address balance, which sponsored transactions can't access yet. (Funds received via gasless transfers land there.) This will work once the gas sponsor adds address-balance support.`,
308
+ { coinObjectTotal: coinObjectTotal.toString(), coinType }
309
+ );
310
+ if (coinObjectTotal === 0n) {
311
+ throw unsponsorable();
312
+ }
313
+ const requested = amount === "all" ? coinObjectTotal : amount;
314
+ if (requested > coinObjectTotal) {
315
+ if (allowSwapAll && amount === "all") ; else {
316
+ throw unsponsorable();
317
+ }
318
+ }
319
+ const swapAll = amount === "all" || requested >= coinObjectTotal;
320
+ const effectiveAmount = swapAll ? coinObjectTotal : requested;
321
+ const [first, ...rest] = objects;
322
+ const primary = tx.object(first.objectId);
323
+ if (rest.length > 0) {
324
+ tx.mergeCoins(
325
+ primary,
326
+ rest.map((o) => tx.object(o.objectId))
327
+ );
328
+ }
329
+ const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
330
+ mergeCache?.set(coinType, {
331
+ primary,
332
+ remaining: coinObjectTotal - effectiveAmount
333
+ });
334
+ return { coin, effectiveAmount, swapAll };
335
+ }
336
+ async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext, mergeCache) {
337
+ if (sponsoredContext) {
338
+ const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
339
+ return selectCoinObjectsOnly(tx, client, owner, SUI_TYPE2, amountMist, false, mergeCache);
340
+ }
341
+ const [coin] = tx.splitCoins(tx.gas, [amountMist]);
342
+ return { coin, effectiveAmount: amountMist, swapAll: false };
343
+ }
344
+ var init_coinSelection = __esm({
345
+ "src/wallet/coinSelection.ts"() {
346
+ init_errors();
347
+ }
348
+ });
349
+
206
350
  // src/wallet/keypairSigner.ts
207
351
  var KeypairSigner = class {
208
352
  constructor(keypair) {
@@ -262,12 +406,8 @@ var ZkLoginSigner = class {
262
406
  }
263
407
  };
264
408
 
265
- // src/mpp-cost.ts
266
- function parseChallengeAmount(challenge) {
267
- const raw = challenge?.request?.amount;
268
- const n = typeof raw === "string" ? Number(raw) : typeof raw === "number" ? raw : Number.NaN;
269
- return Number.isFinite(n) ? n : void 0;
270
- }
409
+ // src/wallet/pay.ts
410
+ init_errors();
271
411
 
272
412
  // src/wallet/executeTx.ts
273
413
  async function executeTx(client, signer, buildTx, options = {}) {
@@ -275,63 +415,142 @@ async function executeTx(client, signer, buildTx, options = {}) {
275
415
  tx.setSender(signer.getAddress());
276
416
  const txBytes = await tx.build({ client: options.buildClient ?? client });
277
417
  const { signature } = await signer.signTransaction(txBytes);
278
- const result = await client.executeTransactionBlock({
279
- transactionBlock: txBytes,
280
- signature,
281
- options: { showEffects: true }
418
+ const result = await client.core.executeTransaction({
419
+ transaction: txBytes,
420
+ signatures: [signature],
421
+ include: { effects: true }
282
422
  });
283
- await client.waitForTransaction({ digest: result.digest });
284
- const gasUsed = result.effects?.gasUsed;
423
+ const txn = result.$kind === "Transaction" ? result.Transaction : result.FailedTransaction;
424
+ await client.core.waitForTransaction({ digest: txn.digest });
425
+ const effects = txn.effects ?? void 0;
426
+ const gasUsed = effects?.gasUsed;
285
427
  let gasCostSui = 0;
286
428
  if (gasUsed) {
287
429
  const total = BigInt(gasUsed.computationCost) + BigInt(gasUsed.storageCost) - BigInt(gasUsed.storageRebate);
288
430
  gasCostSui = Number(total) / 1e9;
289
431
  }
290
- return { digest: result.digest, gasCostSui, effects: result.effects ?? void 0 };
432
+ return { digest: txn.digest, gasCostSui, effects };
291
433
  }
292
434
 
293
435
  // src/wallet/pay.ts
294
436
  async function payWithMpp(args) {
295
437
  const { signer, client, options } = args;
296
- const { Mppx } = await import('mppx/client');
297
- const { sui, USDC } = await import('@suimpp/mpp/client');
298
- const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
299
- const signerAddress = signer.getAddress();
300
- let paymentDigest;
301
- let gasCostSui = 0;
302
- let chargedAmount;
303
- const network = client.network === "testnet" ? "testnet" : "mainnet";
304
- const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
305
- const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
306
- const mppx = Mppx.create({
307
- polyfill: false,
308
- onChallenge: async (challenge) => {
309
- const parsed = parseChallengeAmount(challenge);
310
- if (parsed !== void 0) chargedAmount = parsed;
311
- return void 0;
312
- },
313
- methods: [sui({
314
- client,
315
- currency: USDC,
316
- signer: {
317
- toSuiAddress: () => signerAddress,
318
- signPersonalMessage: (bytes) => signer.signPersonalMessage(bytes)
319
- },
320
- execute: async (tx) => {
321
- const result = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
322
- paymentDigest = result.digest;
323
- gasCostSui = result.gasCostSui;
324
- return { digest: result.digest };
325
- }
326
- })]
327
- });
328
438
  const method = (options.method ?? "GET").toUpperCase();
329
439
  const canHaveBody = method !== "GET" && method !== "HEAD";
330
- const response = await mppx.fetch(options.url, {
440
+ const reqInit = {
331
441
  method,
332
442
  headers: options.headers,
333
443
  body: canHaveBody ? options.body : void 0
444
+ };
445
+ const probe = await fetch(options.url, reqInit);
446
+ if (probe.status !== 402) {
447
+ return finalize(probe, { paid: false });
448
+ }
449
+ const requirements = await pickSuiExactRequirements(probe, client.network);
450
+ if (!requirements) {
451
+ throw new exports.T2000Error(
452
+ "FACILITATOR_REJECTION",
453
+ `Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. This SDK only speaks the x402 dialect.`
454
+ );
455
+ }
456
+ return payViaX402({ signer, client, options, reqInit, requirements });
457
+ }
458
+ async function pickSuiExactRequirements(response, network) {
459
+ try {
460
+ const body = await response.clone().json();
461
+ const want = `sui:${network === "testnet" ? "testnet" : "mainnet"}`;
462
+ return body.accepts?.find((a) => a.scheme === "exact" && a.network === want);
463
+ } catch {
464
+ return void 0;
465
+ }
466
+ }
467
+ async function payViaX402(args) {
468
+ const { signer, client, options, reqInit, requirements } = args;
469
+ const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import('@suimpp/mpp/x402');
470
+ const amountRaw = BigInt(requirements.maxAmountRequired);
471
+ const migrationGasSui = await ensureAddressBalanceCovers({
472
+ signer,
473
+ client,
474
+ asset: requirements.asset,
475
+ amountRaw
476
+ });
477
+ const signerAdapter = {
478
+ toSuiAddress: () => signer.getAddress(),
479
+ signTransaction: (bytes) => signer.signTransaction(bytes)
480
+ };
481
+ const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });
482
+ const res = await fetch(options.url, {
483
+ ...reqInit,
484
+ headers: { ...options.headers ?? {}, [X402_PAYMENT_HEADER]: header }
334
485
  });
486
+ const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
487
+ const paid = !!settleHeader;
488
+ let digest;
489
+ if (settleHeader) {
490
+ try {
491
+ digest = JSON.parse(new TextDecoder().decode(utils.fromBase64(settleHeader))).transaction;
492
+ } catch {
493
+ digest = void 0;
494
+ }
495
+ }
496
+ const result = await finalize(res, { paid });
497
+ if (!paid) return { ...result, dialect: "x402" };
498
+ return {
499
+ ...result,
500
+ dialect: "x402",
501
+ cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),
502
+ gasCostSui: migrationGasSui,
503
+ receipt: digest ? { reference: digest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : result.receipt
504
+ };
505
+ }
506
+ async function ensureAddressBalanceCovers(args) {
507
+ const { signer, client, asset, amountRaw } = args;
508
+ const owner = signer.getAddress();
509
+ const balanceResp = await client.core.getBalance({ owner, coinType: asset });
510
+ const total = BigInt(balanceResp.balance.balance);
511
+ if (total < amountRaw) {
512
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} to pay`, {
513
+ available: total.toString(),
514
+ required: amountRaw.toString()
515
+ });
516
+ }
517
+ let coinSum = 0n;
518
+ let cursor;
519
+ let hasNext = true;
520
+ while (hasNext) {
521
+ const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
522
+ for (const c of page.objects) coinSum += BigInt(c.balance);
523
+ cursor = page.cursor;
524
+ hasNext = page.hasNextPage;
525
+ }
526
+ const addressBalance = total - coinSum;
527
+ if (addressBalance >= amountRaw) return 0;
528
+ const shortfall = amountRaw - addressBalance;
529
+ const { Transaction: Transaction2 } = await import('@mysten/sui/transactions');
530
+ const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
531
+ const grpcClient = await makeGrpcBuildClient(client);
532
+ const migration = await executeTx(
533
+ client,
534
+ signer,
535
+ async () => {
536
+ const tx = new Transaction2();
537
+ const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
538
+ sponsoredContext: true,
539
+ // coin-objects-only — never the address balance
540
+ allowSwapAll: false
541
+ });
542
+ tx.moveCall({
543
+ target: "0x2::coin::send_funds",
544
+ typeArguments: [asset],
545
+ arguments: [coin, tx.pure.address(owner)]
546
+ });
547
+ return tx;
548
+ },
549
+ { buildClient: grpcClient }
550
+ );
551
+ return migration.gasCostSui;
552
+ }
553
+ async function finalize(response, opts) {
335
554
  const contentType = response.headers.get("content-type") ?? "";
336
555
  let body;
337
556
  try {
@@ -339,15 +558,25 @@ async function payWithMpp(args) {
339
558
  } catch {
340
559
  body = null;
341
560
  }
342
- const paid = !!paymentDigest;
343
- return {
344
- status: response.status,
345
- body,
346
- paid,
347
- cost: paid ? chargedAmount ?? options.maxPrice ?? void 0 : void 0,
348
- gasCostSui: paid ? gasCostSui : void 0,
349
- receipt: paymentDigest ? { reference: paymentDigest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : void 0
350
- };
561
+ return { status: response.status, body, paid: opts.paid };
562
+ }
563
+ async function makeGrpcBuildClient(client) {
564
+ const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
565
+ const network = client.network === "testnet" ? "testnet" : "mainnet";
566
+ const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
567
+ return new SuiGrpcClient2({ baseUrl, network });
568
+ }
569
+ function atomicToHuman(raw, decimals) {
570
+ return Number(raw) / 10 ** decimals;
571
+ }
572
+ async function assetDecimals(coinType) {
573
+ try {
574
+ const { getDecimalsForCoinType: getDecimalsForCoinType2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
575
+ const d = getDecimalsForCoinType2(coinType);
576
+ return typeof d === "number" ? d : 6;
577
+ } catch {
578
+ return 6;
579
+ }
351
580
  }
352
581
 
353
582
  // src/browser.ts
@@ -358,9 +587,6 @@ init_errors();
358
587
  var MIST_PER_SUI = 1000000000n;
359
588
  var SUI_DECIMALS = 9;
360
589
  var USDC_DECIMALS = 6;
361
- var BPS_DENOMINATOR = 10000n;
362
- var SAVE_FEE_BPS = 10n;
363
- var BORROW_FEE_BPS = 5n;
364
590
  var CLOCK_ID = "0x6";
365
591
  var SUPPORTED_ASSETS = {
366
592
  USDC: {
@@ -419,7 +645,6 @@ var SUPPORTED_ASSETS = {
419
645
  }
420
646
  };
421
647
  var STABLE_ASSETS = ["USDC", "USDsui"];
422
- var ALL_NAVI_ASSETS = Object.keys(SUPPORTED_ASSETS);
423
648
  ({
424
649
  USDC: SUPPORTED_ASSETS.USDC.type,
425
650
  USDsui: SUPPORTED_ASSETS.USDsui.type
@@ -583,8 +808,13 @@ function extractAllUserLegs(changes, sender) {
583
808
  }
584
809
  return legs;
585
810
  }
586
-
587
- // src/wallet/history.ts
811
+ function buildRecord(args) {
812
+ const { digest, moveCallTargets, commandTypes, balanceChanges, timestampMs, gasCost, address } = args;
813
+ const legs = extractAllUserLegs(balanceChanges, address);
814
+ const { amount, asset, recipient, direction } = extractTransferDetails(balanceChanges, address);
815
+ const { action, label } = classifyTransaction(moveCallTargets, commandTypes, balanceChanges, address);
816
+ return { digest, action, label, legs, amount, asset, recipient, direction, timestamp: timestampMs, gasCost };
817
+ }
588
818
  function parseSuiRpcTx(tx, address) {
589
819
  return parseTxRecord(tx, address);
590
820
  }
@@ -605,27 +835,15 @@ function parseTxRecord(tx, address) {
605
835
  const gasUsed = tx.effects?.gasUsed;
606
836
  const gasCost = gasUsed ? (Number(gasUsed.computationCost) + Number(gasUsed.storageCost) - Number(gasUsed.storageRebate)) / 1e9 : void 0;
607
837
  const { moveCallTargets, commandTypes } = extractCommands(tx.transaction);
608
- const balanceChanges = tx.balanceChanges ?? [];
609
- const legs = extractAllUserLegs(balanceChanges, address);
610
- const { amount, asset, recipient, direction } = extractTransferDetails(balanceChanges, address);
611
- const { action, label } = classifyTransaction(
838
+ return buildRecord({
839
+ digest: tx.digest,
612
840
  moveCallTargets,
613
841
  commandTypes,
614
- balanceChanges,
842
+ balanceChanges: tx.balanceChanges ?? [],
843
+ timestampMs: Number(tx.timestampMs ?? 0),
844
+ gasCost,
615
845
  address
616
- );
617
- return {
618
- digest: tx.digest,
619
- action,
620
- label,
621
- legs,
622
- amount,
623
- asset,
624
- recipient,
625
- direction,
626
- timestamp: Number(tx.timestampMs ?? 0),
627
- gasCost
628
- };
846
+ });
629
847
  }
630
848
  function extractCommands(txBlock) {
631
849
  const result = { moveCallTargets: [], commandTypes: [] };
@@ -700,44 +918,13 @@ function toBase64(bytes) {
700
918
  for (const byte of bytes) binary += String.fromCharCode(byte);
701
919
  return btoa(binary);
702
920
  }
703
- function fromBase64(b64) {
921
+ function fromBase642(b64) {
704
922
  const binary = atob(b64);
705
923
  const bytes = new Uint8Array(binary.length);
706
924
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
707
925
  return bytes;
708
926
  }
709
927
 
710
- // src/protocols/protocolFee.ts
711
- var FEE_RATES = {
712
- save: SAVE_FEE_BPS,
713
- borrow: BORROW_FEE_BPS,
714
- // Swap uses Cetus's overlay-fee mechanism (taken from output by the aggregator
715
- // and transferred to `overlayFee.receiver`). We list the rate here for display
716
- // / quote calculations only — `addFeeTransfer` is NOT called for swaps.
717
- swap: 10n
718
- // 0.1%
719
- };
720
- function calculateFee(operation, amount) {
721
- const bps = FEE_RATES[operation];
722
- const feeAmount = amount * Number(bps) / Number(BPS_DENOMINATOR);
723
- const rawAmount = stableToRaw(feeAmount, USDC_DECIMALS);
724
- return {
725
- amount: feeAmount,
726
- asset: "USDC",
727
- rate: Number(bps) / Number(BPS_DENOMINATOR),
728
- rawAmount
729
- };
730
- }
731
- function addFeeTransfer(tx, paymentCoin, feeBps, receiver, amount, decimals = USDC_DECIMALS) {
732
- if (feeBps <= 0n) return;
733
- if (amount <= 0) return;
734
- const feeAmount = amount * Number(feeBps) / Number(BPS_DENOMINATOR);
735
- const rawFee = stableToRaw(feeAmount, decimals);
736
- if (rawFee <= 0n) return;
737
- const [feeCoin] = tx.splitCoins(paymentCoin, [tx.pure.u64(rawFee)]);
738
- tx.transferObjects([feeCoin], tx.pure.address(receiver));
739
- }
740
-
741
928
  // src/protocols/cetus-swap.ts
742
929
  init_token_registry();
743
930
  init_token_registry();
@@ -840,10 +1027,6 @@ function buildMessage(rule, details) {
840
1027
  }
841
1028
 
842
1029
  // src/safeguards/types.ts
843
- var OUTBOUND_OPS = /* @__PURE__ */ new Set([
844
- "send",
845
- "pay"
846
- ]);
847
1030
  var DEFAULT_SAFEGUARD_CONFIG = {
848
1031
  locked: false,
849
1032
  maxPerTx: 0,
@@ -855,9 +1038,6 @@ var DEFAULT_SAFEGUARD_CONFIG = {
855
1038
  // src/browser.ts
856
1039
  init_token_registry();
857
1040
 
858
- exports.ALL_NAVI_ASSETS = ALL_NAVI_ASSETS;
859
- exports.BORROW_FEE_BPS = BORROW_FEE_BPS;
860
- exports.BPS_DENOMINATOR = BPS_DENOMINATOR;
861
1041
  exports.CLOCK_ID = CLOCK_ID;
862
1042
  exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
863
1043
  exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
@@ -866,9 +1046,7 @@ exports.KNOWN_TARGETS = KNOWN_TARGETS;
866
1046
  exports.KeypairSigner = KeypairSigner;
867
1047
  exports.LABEL_PATTERNS = LABEL_PATTERNS;
868
1048
  exports.MIST_PER_SUI = MIST_PER_SUI;
869
- exports.OUTBOUND_OPS = OUTBOUND_OPS;
870
1049
  exports.OVERLAY_FEE_RATE = OVERLAY_FEE_RATE;
871
- exports.SAVE_FEE_BPS = SAVE_FEE_BPS;
872
1050
  exports.STABLE_ASSETS = STABLE_ASSETS;
873
1051
  exports.SUI_DECIMALS = SUI_DECIMALS;
874
1052
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
@@ -876,9 +1054,7 @@ exports.SafeguardError = SafeguardError;
876
1054
  exports.T2000_OVERLAY_FEE_WALLET = T2000_OVERLAY_FEE_WALLET;
877
1055
  exports.USDC_DECIMALS = USDC_DECIMALS;
878
1056
  exports.ZkLoginSigner = ZkLoginSigner;
879
- exports.addFeeTransfer = addFeeTransfer;
880
1057
  exports.buildSwapTx = buildSwapTx;
881
- exports.calculateFee = calculateFee;
882
1058
  exports.classifyAction = classifyAction;
883
1059
  exports.classifyLabel = classifyLabel;
884
1060
  exports.classifyTransaction = classifyTransaction;
@@ -892,13 +1068,9 @@ exports.findSwapRoute = findSwapRoute;
892
1068
  exports.formatAssetAmount = formatAssetAmount;
893
1069
  exports.formatSui = formatSui;
894
1070
  exports.formatUsd = formatUsd;
895
- exports.fromBase64 = fromBase64;
1071
+ exports.fromBase64 = fromBase642;
896
1072
  exports.getDecimals = getDecimals;
897
1073
  exports.getDecimalsForCoinType = getDecimalsForCoinType;
898
- exports.getTier = getTier;
899
- exports.isSupported = isSupported;
900
- exports.isTier1 = isTier1;
901
- exports.isTier2 = isTier2;
902
1074
  exports.mapMoveAbortCode = mapMoveAbortCode;
903
1075
  exports.mapWalletError = mapWalletError;
904
1076
  exports.mistToSui = mistToSui;