@t2000/sdk 5.0.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/dist/index.cjs CHANGED
@@ -576,8 +576,8 @@ async function addSwapToTx(tx, client, address, input) {
576
576
  inputCoin = result.coin;
577
577
  effectiveRaw = result.effectiveAmount;
578
578
  } else {
579
- const { selectAndSplitCoin: selectAndSplitCoin3 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
580
- const result = await selectAndSplitCoin3(tx, client, address, fromType, requestedRaw, {
579
+ const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
580
+ const result = await selectAndSplitCoin2(tx, client, address, fromType, requestedRaw, {
581
581
  sponsoredContext: input.sponsoredContext ?? false,
582
582
  mergeCache: input.coinMergeCache
583
583
  });
@@ -997,55 +997,126 @@ async function executeTx(client, signer, buildTx, options = {}) {
997
997
  return { digest: txn.digest, gasCostSui, effects };
998
998
  }
999
999
 
1000
- // src/mpp-cost.ts
1001
- function parseChallengeAmount(challenge) {
1002
- const raw = challenge?.request?.amount;
1003
- const n = typeof raw === "string" ? Number(raw) : typeof raw === "number" ? raw : Number.NaN;
1004
- return Number.isFinite(n) ? n : void 0;
1005
- }
1006
-
1007
1000
  // src/wallet/pay.ts
1001
+ init_errors();
1008
1002
  async function payWithMpp(args) {
1009
1003
  const { signer, client, options } = args;
1010
- const { Mppx } = await import('mppx/client');
1011
- const { sui, USDC } = await import('@suimpp/mpp/client');
1012
- const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
1013
- const signerAddress = signer.getAddress();
1014
- let paymentDigest;
1015
- let gasCostSui = 0;
1016
- let chargedAmount;
1017
- const network = client.network === "testnet" ? "testnet" : "mainnet";
1018
- const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
1019
- const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
1020
- const mppx = Mppx.create({
1021
- polyfill: false,
1022
- onChallenge: async (challenge) => {
1023
- const parsed = parseChallengeAmount(challenge);
1024
- if (parsed !== void 0) chargedAmount = parsed;
1025
- return void 0;
1026
- },
1027
- methods: [sui({
1028
- client,
1029
- currency: USDC,
1030
- signer: {
1031
- toSuiAddress: () => signerAddress,
1032
- signPersonalMessage: (bytes) => signer.signPersonalMessage(bytes)
1033
- },
1034
- execute: async (tx) => {
1035
- const result = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
1036
- paymentDigest = result.digest;
1037
- gasCostSui = result.gasCostSui;
1038
- return { digest: result.digest };
1039
- }
1040
- })]
1041
- });
1042
1004
  const method = (options.method ?? "GET").toUpperCase();
1043
1005
  const canHaveBody = method !== "GET" && method !== "HEAD";
1044
- const response = await mppx.fetch(options.url, {
1006
+ const reqInit = {
1045
1007
  method,
1046
1008
  headers: options.headers,
1047
1009
  body: canHaveBody ? options.body : void 0
1010
+ };
1011
+ const probe = await fetch(options.url, reqInit);
1012
+ if (probe.status !== 402) {
1013
+ return finalize(probe, { paid: false });
1014
+ }
1015
+ const requirements = await pickSuiExactRequirements(probe, client.network);
1016
+ if (!requirements) {
1017
+ throw new exports.T2000Error(
1018
+ "FACILITATOR_REJECTION",
1019
+ `Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. This SDK only speaks the x402 dialect.`
1020
+ );
1021
+ }
1022
+ return payViaX402({ signer, client, options, reqInit, requirements });
1023
+ }
1024
+ async function pickSuiExactRequirements(response, network) {
1025
+ try {
1026
+ const body = await response.clone().json();
1027
+ const want = `sui:${network === "testnet" ? "testnet" : "mainnet"}`;
1028
+ return body.accepts?.find((a) => a.scheme === "exact" && a.network === want);
1029
+ } catch {
1030
+ return void 0;
1031
+ }
1032
+ }
1033
+ async function payViaX402(args) {
1034
+ const { signer, client, options, reqInit, requirements } = args;
1035
+ const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import('@suimpp/mpp/x402');
1036
+ const amountRaw = BigInt(requirements.maxAmountRequired);
1037
+ const migrationGasSui = await ensureAddressBalanceCovers({
1038
+ signer,
1039
+ client,
1040
+ asset: requirements.asset,
1041
+ amountRaw
1042
+ });
1043
+ const signerAdapter = {
1044
+ toSuiAddress: () => signer.getAddress(),
1045
+ signTransaction: (bytes) => signer.signTransaction(bytes)
1046
+ };
1047
+ const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });
1048
+ const res = await fetch(options.url, {
1049
+ ...reqInit,
1050
+ headers: { ...options.headers ?? {}, [X402_PAYMENT_HEADER]: header }
1048
1051
  });
1052
+ const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
1053
+ const paid = !!settleHeader;
1054
+ let digest;
1055
+ if (settleHeader) {
1056
+ try {
1057
+ digest = JSON.parse(new TextDecoder().decode(utils.fromBase64(settleHeader))).transaction;
1058
+ } catch {
1059
+ digest = void 0;
1060
+ }
1061
+ }
1062
+ const result = await finalize(res, { paid });
1063
+ if (!paid) return { ...result, dialect: "x402" };
1064
+ return {
1065
+ ...result,
1066
+ dialect: "x402",
1067
+ cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),
1068
+ gasCostSui: migrationGasSui,
1069
+ receipt: digest ? { reference: digest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : result.receipt
1070
+ };
1071
+ }
1072
+ async function ensureAddressBalanceCovers(args) {
1073
+ const { signer, client, asset, amountRaw } = args;
1074
+ const owner = signer.getAddress();
1075
+ const balanceResp = await client.core.getBalance({ owner, coinType: asset });
1076
+ const total = BigInt(balanceResp.balance.balance);
1077
+ if (total < amountRaw) {
1078
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} to pay`, {
1079
+ available: total.toString(),
1080
+ required: amountRaw.toString()
1081
+ });
1082
+ }
1083
+ let coinSum = 0n;
1084
+ let cursor;
1085
+ let hasNext = true;
1086
+ while (hasNext) {
1087
+ const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
1088
+ for (const c of page.objects) coinSum += BigInt(c.balance);
1089
+ cursor = page.cursor;
1090
+ hasNext = page.hasNextPage;
1091
+ }
1092
+ const addressBalance = total - coinSum;
1093
+ if (addressBalance >= amountRaw) return 0;
1094
+ const shortfall = amountRaw - addressBalance;
1095
+ const { Transaction: Transaction6 } = await import('@mysten/sui/transactions');
1096
+ const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
1097
+ const grpcClient = await makeGrpcBuildClient(client);
1098
+ const migration = await executeTx(
1099
+ client,
1100
+ signer,
1101
+ async () => {
1102
+ const tx = new Transaction6();
1103
+ const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
1104
+ sponsoredContext: true,
1105
+ // coin-objects-only — never the address balance
1106
+ allowSwapAll: false
1107
+ });
1108
+ tx.moveCall({
1109
+ target: "0x2::coin::send_funds",
1110
+ typeArguments: [asset],
1111
+ arguments: [coin, tx.pure.address(owner)]
1112
+ });
1113
+ return tx;
1114
+ },
1115
+ { buildClient: grpcClient }
1116
+ );
1117
+ return migration.gasCostSui;
1118
+ }
1119
+ async function finalize(response, opts) {
1049
1120
  const contentType = response.headers.get("content-type") ?? "";
1050
1121
  let body;
1051
1122
  try {
@@ -1053,15 +1124,25 @@ async function payWithMpp(args) {
1053
1124
  } catch {
1054
1125
  body = null;
1055
1126
  }
1056
- const paid = !!paymentDigest;
1057
- return {
1058
- status: response.status,
1059
- body,
1060
- paid,
1061
- cost: paid ? chargedAmount ?? options.maxPrice ?? void 0 : void 0,
1062
- gasCostSui: paid ? gasCostSui : void 0,
1063
- receipt: paymentDigest ? { reference: paymentDigest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : void 0
1064
- };
1127
+ return { status: response.status, body, paid: opts.paid };
1128
+ }
1129
+ async function makeGrpcBuildClient(client) {
1130
+ const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
1131
+ const network = client.network === "testnet" ? "testnet" : "mainnet";
1132
+ const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
1133
+ return new SuiGrpcClient2({ baseUrl, network });
1134
+ }
1135
+ function atomicToHuman(raw, decimals) {
1136
+ return Number(raw) / 10 ** decimals;
1137
+ }
1138
+ async function assetDecimals(coinType) {
1139
+ try {
1140
+ const { getDecimalsForCoinType: getDecimalsForCoinType2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
1141
+ const d = getDecimalsForCoinType2(coinType);
1142
+ return typeof d === "number" ? d : 6;
1143
+ } catch {
1144
+ return 6;
1145
+ }
1065
1146
  }
1066
1147
 
1067
1148
  // src/wallet/zkLoginSigner.ts