@t2000/sdk 5.5.1 → 5.6.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/browser.cjs CHANGED
@@ -442,10 +442,60 @@ async function executeTx(client, signer, buildTx, options = {}) {
442
442
  }
443
443
  return { digest: txn.digest, gasCostSui, effects };
444
444
  }
445
+ var PREFLIGHT_MAX_AMOUNT = 1e6;
446
+ var PREFLIGHT_OK = { valid: true };
447
+ function preflightFail(code, error) {
448
+ return { valid: false, code, error };
449
+ }
450
+ function checkPositiveAmount(amount, label = "Amount", max = PREFLIGHT_MAX_AMOUNT) {
451
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
452
+ return preflightFail("INVALID_AMOUNT", `${label} must be a finite number`);
453
+ }
454
+ if (amount <= 0) {
455
+ return preflightFail("INVALID_AMOUNT", `${label} must be greater than zero`);
456
+ }
457
+ if (amount > max) {
458
+ return preflightFail("INVALID_AMOUNT", `${label} ${amount} exceeds the sane maximum (${max})`);
459
+ }
460
+ return PREFLIGHT_OK;
461
+ }
462
+ function checkSuiAddress(address, label = "recipient") {
463
+ try {
464
+ if (typeof address === "string" && address.trim() !== "" && utils.isValidSuiAddress(utils.normalizeSuiAddress(address))) {
465
+ return PREFLIGHT_OK;
466
+ }
467
+ } catch {
468
+ }
469
+ return preflightFail("INVALID_ADDRESS", `Invalid ${label} address: ${address}`);
470
+ }
445
471
 
446
472
  // src/wallet/pay.ts
473
+ function preflightPay(input) {
474
+ if (typeof input.url !== "string" || input.url.trim() === "") {
475
+ return preflightFail("FACILITATOR_REJECTION", "A target URL is required to pay");
476
+ }
477
+ let parsed;
478
+ try {
479
+ parsed = new URL(input.url);
480
+ } catch {
481
+ return preflightFail("FACILITATOR_REJECTION", `Invalid URL: ${input.url}`);
482
+ }
483
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
484
+ return preflightFail(
485
+ "FACILITATOR_REJECTION",
486
+ `URL must be http(s): got ${parsed.protocol}//`
487
+ );
488
+ }
489
+ if (input.maxPrice !== void 0) {
490
+ const priceCheck = checkPositiveAmount(input.maxPrice, "maxPrice");
491
+ if (!priceCheck.valid) return priceCheck;
492
+ }
493
+ return PREFLIGHT_OK;
494
+ }
447
495
  async function payWithMpp(args) {
448
496
  const { signer, client, options } = args;
497
+ const pf = preflightPay({ url: options.url, maxPrice: options.maxPrice });
498
+ if (!pf.valid) throw new exports.T2000Error(pf.code, pf.error);
449
499
  const method = (options.method ?? "GET").toUpperCase();
450
500
  const canHaveBody = method !== "GET" && method !== "HEAD";
451
501
  const reqInit = {
@@ -537,14 +587,14 @@ async function ensureAddressBalanceCovers(args) {
537
587
  const addressBalance = total - coinSum;
538
588
  if (addressBalance >= amountRaw) return 0;
539
589
  const shortfall = amountRaw - addressBalance;
540
- const { Transaction: Transaction2 } = await import('@mysten/sui/transactions');
590
+ const { Transaction: Transaction3 } = await import('@mysten/sui/transactions');
541
591
  const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
542
592
  const grpcClient = await makeGrpcBuildClient(client);
543
593
  const migration = await executeTx(
544
594
  client,
545
595
  signer,
546
596
  async () => {
547
- const tx = new Transaction2();
597
+ const tx = new Transaction3();
548
598
  const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
549
599
  sponsoredContext: true,
550
600
  // coin-objects-only — never the address balance
@@ -656,12 +706,35 @@ var SUPPORTED_ASSETS = {
656
706
  }
657
707
  };
658
708
  var STABLE_ASSETS = ["USDC", "USDsui"];
659
- ({
709
+ var OPERATION_ASSETS = {
710
+ send: ["USDC", "USDsui", "SUI"],
711
+ swap: "*"
712
+ };
713
+ function isAllowedAsset(op, asset) {
714
+ const allowed = OPERATION_ASSETS[op];
715
+ if (allowed === "*") return true;
716
+ const target = asset.toLowerCase();
717
+ return allowed.some((a) => a.toLowerCase() === target);
718
+ }
719
+ function assertAllowedAsset(op, asset) {
720
+ if (!asset) return;
721
+ if (!isAllowedAsset(op, asset)) {
722
+ const allowed = OPERATION_ASSETS[op];
723
+ const list = Array.isArray(allowed) ? allowed.join(", ") : "any";
724
+ const swapHint = " Swap to USDC or USDsui first, or send SUI." ;
725
+ throw new exports.T2000Error(
726
+ "INVALID_ASSET",
727
+ `${op} only supports ${list}. Cannot use ${asset}.${swapHint}`
728
+ );
729
+ }
730
+ }
731
+ var GASLESS_STABLE_TYPES = {
660
732
  USDC: SUPPORTED_ASSETS.USDC.type,
661
733
  USDsui: SUPPORTED_ASSETS.USDsui.type
662
- });
734
+ };
663
735
  var T2000_OVERLAY_FEE_WALLET = process.env.T2000_OVERLAY_FEE_WALLET ?? "0x5366efbf2b4fe5767fe2e78eb197aa5f5d138d88ac3333fbf3f80a1927da473a";
664
736
  var DEFAULT_NETWORK = "mainnet";
737
+ var GASLESS_MIN_STABLE_AMOUNT = 0.01;
665
738
  var GAS_RESERVE_MIN = 0.05;
666
739
  init_errors();
667
740
  function validateAddress(address) {
@@ -902,6 +975,9 @@ function rawToStable(raw, decimals) {
902
975
  function getDecimals(asset) {
903
976
  return SUPPORTED_ASSETS[asset].decimals;
904
977
  }
978
+ function displayToRaw(amount, decimals) {
979
+ return BigInt(Math.round(amount * 10 ** decimals));
980
+ }
905
981
  function formatUsd(amount) {
906
982
  return `$${amount.toFixed(2)}`;
907
983
  }
@@ -939,6 +1015,23 @@ function fromBase642(b64) {
939
1015
  // src/protocols/cetus-swap.ts
940
1016
  init_token_registry();
941
1017
  init_token_registry();
1018
+ function preflightSwap(input) {
1019
+ if (typeof input.from !== "string" || input.from.trim() === "") {
1020
+ return preflightFail("INVALID_ASSET", "A `from` token is required to swap");
1021
+ }
1022
+ if (typeof input.to !== "string" || input.to.trim() === "") {
1023
+ return preflightFail("INVALID_ASSET", "A `to` token is required to swap");
1024
+ }
1025
+ const amountCheck = checkPositiveAmount(input.amount, "Amount", Number.POSITIVE_INFINITY);
1026
+ if (!amountCheck.valid) return amountCheck;
1027
+ const resolvedFrom = resolveTokenType(input.from);
1028
+ const resolvedTo = resolveTokenType(input.to);
1029
+ const sameToken = input.from.trim() === input.to.trim() || resolvedFrom !== null && resolvedFrom === resolvedTo;
1030
+ if (sameToken) {
1031
+ return preflightFail("INVALID_ASSET", `Cannot swap ${input.from} to itself`);
1032
+ }
1033
+ return PREFLIGHT_OK;
1034
+ }
942
1035
  var OVERLAY_FEE_RATE = 1e-3;
943
1036
  var clientCache = /* @__PURE__ */ new Map();
944
1037
  function getClient(walletAddress, overlayFee) {
@@ -1004,6 +1097,64 @@ async function buildSwapTx(params) {
1004
1097
  });
1005
1098
  return outputCoin;
1006
1099
  }
1100
+ init_errors();
1101
+ function preflightSend(input) {
1102
+ try {
1103
+ assertAllowedAsset("send", input.asset);
1104
+ } catch (e) {
1105
+ return preflightFail("INVALID_ASSET", e.message);
1106
+ }
1107
+ const amountCheck = checkPositiveAmount(input.amount);
1108
+ if (!amountCheck.valid) return amountCheck;
1109
+ if ((input.asset === "USDC" || input.asset === "USDsui") && input.amount < GASLESS_MIN_STABLE_AMOUNT) {
1110
+ return preflightFail(
1111
+ "INVALID_AMOUNT",
1112
+ `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${input.asset}. Got ${input.amount}.`
1113
+ );
1114
+ }
1115
+ const addressCheck = checkSuiAddress(input.to);
1116
+ if (!addressCheck.valid) return addressCheck;
1117
+ return PREFLIGHT_OK;
1118
+ }
1119
+ async function buildSendTx({
1120
+ client,
1121
+ address,
1122
+ to,
1123
+ amount,
1124
+ asset
1125
+ }) {
1126
+ const pf = preflightSend({ to, amount, asset });
1127
+ if (!pf.valid) throw new exports.T2000Error(pf.code, pf.error);
1128
+ const recipient = validateAddress(to);
1129
+ const assetInfo = SUPPORTED_ASSETS[asset];
1130
+ if (!assetInfo) throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
1131
+ const rawAmount = displayToRaw(amount, assetInfo.decimals);
1132
+ const tx = new transactions.Transaction();
1133
+ tx.setSender(address);
1134
+ const balanceResp = await client.core.getBalance({ owner: address, coinType: assetInfo.type });
1135
+ const totalBalance = BigInt(balanceResp.balance.balance);
1136
+ if (totalBalance < rawAmount) {
1137
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, {
1138
+ available: Number(totalBalance) / 10 ** assetInfo.decimals,
1139
+ required: amount
1140
+ });
1141
+ }
1142
+ if (asset === "SUI") {
1143
+ const [sendCoin] = tx.splitCoins(tx.gas, [rawAmount]);
1144
+ tx.transferObjects([sendCoin], recipient);
1145
+ return tx;
1146
+ }
1147
+ const coinType = GASLESS_STABLE_TYPES[asset];
1148
+ tx.moveCall({
1149
+ target: "0x2::balance::send_funds",
1150
+ typeArguments: [coinType],
1151
+ arguments: [
1152
+ tx.balance({ type: coinType, balance: rawAmount }),
1153
+ tx.pure.address(recipient)
1154
+ ]
1155
+ });
1156
+ return tx;
1157
+ }
1007
1158
 
1008
1159
  // src/browser.ts
1009
1160
  init_token_registry();
@@ -1016,13 +1167,18 @@ exports.KeypairSigner = KeypairSigner;
1016
1167
  exports.LABEL_PATTERNS = LABEL_PATTERNS;
1017
1168
  exports.MIST_PER_SUI = MIST_PER_SUI;
1018
1169
  exports.OVERLAY_FEE_RATE = OVERLAY_FEE_RATE;
1170
+ exports.PREFLIGHT_MAX_AMOUNT = PREFLIGHT_MAX_AMOUNT;
1171
+ exports.PREFLIGHT_OK = PREFLIGHT_OK;
1019
1172
  exports.STABLE_ASSETS = STABLE_ASSETS;
1020
1173
  exports.SUI_DECIMALS = SUI_DECIMALS;
1021
1174
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
1022
1175
  exports.T2000_OVERLAY_FEE_WALLET = T2000_OVERLAY_FEE_WALLET;
1023
1176
  exports.USDC_DECIMALS = USDC_DECIMALS;
1024
1177
  exports.ZkLoginSigner = ZkLoginSigner;
1178
+ exports.buildSendTx = buildSendTx;
1025
1179
  exports.buildSwapTx = buildSwapTx;
1180
+ exports.checkPositiveAmount = checkPositiveAmount;
1181
+ exports.checkSuiAddress = checkSuiAddress;
1026
1182
  exports.classifyAction = classifyAction;
1027
1183
  exports.classifyLabel = classifyLabel;
1028
1184
  exports.classifyTransaction = classifyTransaction;
@@ -1044,6 +1200,10 @@ exports.mapWalletError = mapWalletError;
1044
1200
  exports.mistToSui = mistToSui;
1045
1201
  exports.parseSuiRpcTx = parseSuiRpcTx;
1046
1202
  exports.payWithMpp = payWithMpp;
1203
+ exports.preflightFail = preflightFail;
1204
+ exports.preflightPay = preflightPay;
1205
+ exports.preflightSend = preflightSend;
1206
+ exports.preflightSwap = preflightSwap;
1047
1207
  exports.rawToStable = rawToStable;
1048
1208
  exports.rawToUsdc = rawToUsdc;
1049
1209
  exports.refineLendingLabel = refineLendingLabel;