@t2000/sdk 5.5.0 → 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/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { normalizeStructTag, isValidSuiAddress, normalizeSuiAddress, fromBase64 } from '@mysten/sui/utils';
1
2
  import { coinWithBalance, Transaction } from '@mysten/sui/transactions';
2
3
  import { AggregatorClient, Env } from '@cetusprotocol/aggregator-sdk';
3
4
  import BN from 'bn.js';
@@ -5,7 +6,6 @@ import { EventEmitter } from 'eventemitter3';
5
6
  import { createPaymentTransactionUri } from '@mysten/payment-kit';
6
7
  import { SuiGrpcClient } from '@mysten/sui/grpc';
7
8
  import { SuiGraphQLClient } from '@mysten/sui/graphql';
8
- import { normalizeStructTag, normalizeSuiAddress, isValidSuiAddress, fromBase64 } from '@mysten/sui/utils';
9
9
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
10
10
  import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
11
11
  import { access, mkdir, writeFile, readFile } from 'fs/promises';
@@ -112,6 +112,37 @@ var init_errors = __esm({
112
112
  };
113
113
  }
114
114
  });
115
+ function preflightFail(code, error) {
116
+ return { valid: false, code, error };
117
+ }
118
+ function checkPositiveAmount(amount, label = "Amount", max = PREFLIGHT_MAX_AMOUNT) {
119
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
120
+ return preflightFail("INVALID_AMOUNT", `${label} must be a finite number`);
121
+ }
122
+ if (amount <= 0) {
123
+ return preflightFail("INVALID_AMOUNT", `${label} must be greater than zero`);
124
+ }
125
+ if (amount > max) {
126
+ return preflightFail("INVALID_AMOUNT", `${label} ${amount} exceeds the sane maximum (${max})`);
127
+ }
128
+ return PREFLIGHT_OK;
129
+ }
130
+ function checkSuiAddress(address, label = "recipient") {
131
+ try {
132
+ if (typeof address === "string" && address.trim() !== "" && isValidSuiAddress(normalizeSuiAddress(address))) {
133
+ return PREFLIGHT_OK;
134
+ }
135
+ } catch {
136
+ }
137
+ return preflightFail("INVALID_ADDRESS", `Invalid ${label} address: ${address}`);
138
+ }
139
+ var PREFLIGHT_MAX_AMOUNT, PREFLIGHT_OK;
140
+ var init_preflight = __esm({
141
+ "src/preflight.ts"() {
142
+ PREFLIGHT_MAX_AMOUNT = 1e6;
143
+ PREFLIGHT_OK = { valid: true };
144
+ }
145
+ });
115
146
 
116
147
  // src/token-registry.ts
117
148
  var token_registry_exports = {};
@@ -376,10 +407,28 @@ __export(cetus_swap_exports, {
376
407
  findSwapRoute: () => findSwapRoute,
377
408
  isCetusRouteFresh: () => isCetusRouteFresh,
378
409
  isPrecomputedRouteCompatibleWithProviders: () => isPrecomputedRouteCompatibleWithProviders,
410
+ preflightSwap: () => preflightSwap,
379
411
  resolveTokenType: () => resolveTokenType,
380
412
  serializeCetusRoute: () => serializeCetusRoute,
381
413
  verifyCetusRouteCoinMatch: () => verifyCetusRouteCoinMatch
382
414
  });
415
+ function preflightSwap(input) {
416
+ if (typeof input.from !== "string" || input.from.trim() === "") {
417
+ return preflightFail("INVALID_ASSET", "A `from` token is required to swap");
418
+ }
419
+ if (typeof input.to !== "string" || input.to.trim() === "") {
420
+ return preflightFail("INVALID_ASSET", "A `to` token is required to swap");
421
+ }
422
+ const amountCheck = checkPositiveAmount(input.amount, "Amount", Number.POSITIVE_INFINITY);
423
+ if (!amountCheck.valid) return amountCheck;
424
+ const resolvedFrom = resolveTokenType(input.from);
425
+ const resolvedTo = resolveTokenType(input.to);
426
+ const sameToken = input.from.trim() === input.to.trim() || resolvedFrom !== null && resolvedFrom === resolvedTo;
427
+ if (sameToken) {
428
+ return preflightFail("INVALID_ASSET", `Cannot swap ${input.from} to itself`);
429
+ }
430
+ return PREFLIGHT_OK;
431
+ }
383
432
  function serializeCetusRoute(route, context) {
384
433
  return {
385
434
  routerData: serializeRouterDataV3(route.routerData),
@@ -631,6 +680,7 @@ var OVERLAY_FEE_RATE, clientCache;
631
680
  var init_cetus_swap = __esm({
632
681
  "src/protocols/cetus-swap.ts"() {
633
682
  init_token_registry();
683
+ init_preflight();
634
684
  init_token_registry();
635
685
  OVERLAY_FEE_RATE = 1e-3;
636
686
  clientCache = /* @__PURE__ */ new Map();
@@ -1004,8 +1054,33 @@ async function executeTx(client, signer, buildTx, options = {}) {
1004
1054
 
1005
1055
  // src/wallet/pay.ts
1006
1056
  init_errors();
1057
+ init_preflight();
1058
+ function preflightPay(input) {
1059
+ if (typeof input.url !== "string" || input.url.trim() === "") {
1060
+ return preflightFail("FACILITATOR_REJECTION", "A target URL is required to pay");
1061
+ }
1062
+ let parsed;
1063
+ try {
1064
+ parsed = new URL(input.url);
1065
+ } catch {
1066
+ return preflightFail("FACILITATOR_REJECTION", `Invalid URL: ${input.url}`);
1067
+ }
1068
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1069
+ return preflightFail(
1070
+ "FACILITATOR_REJECTION",
1071
+ `URL must be http(s): got ${parsed.protocol}//`
1072
+ );
1073
+ }
1074
+ if (input.maxPrice !== void 0) {
1075
+ const priceCheck = checkPositiveAmount(input.maxPrice, "maxPrice");
1076
+ if (!priceCheck.valid) return priceCheck;
1077
+ }
1078
+ return PREFLIGHT_OK;
1079
+ }
1007
1080
  async function payWithMpp(args) {
1008
1081
  const { signer, client, options } = args;
1082
+ const pf = preflightPay({ url: options.url, maxPrice: options.maxPrice });
1083
+ if (!pf.valid) throw new T2000Error(pf.code, pf.error);
1009
1084
  const method = (options.method ?? "GET").toUpperCase();
1010
1085
  const canHaveBody = method !== "GET" && method !== "HEAD";
1011
1086
  const reqInit = {
@@ -1240,6 +1315,25 @@ function normalizeAsset(input) {
1240
1315
  }
1241
1316
 
1242
1317
  // src/wallet/send.ts
1318
+ init_preflight();
1319
+ function preflightSend(input) {
1320
+ try {
1321
+ assertAllowedAsset("send", input.asset);
1322
+ } catch (e) {
1323
+ return preflightFail("INVALID_ASSET", e.message);
1324
+ }
1325
+ const amountCheck = checkPositiveAmount(input.amount);
1326
+ if (!amountCheck.valid) return amountCheck;
1327
+ if ((input.asset === "USDC" || input.asset === "USDsui") && input.amount < GASLESS_MIN_STABLE_AMOUNT) {
1328
+ return preflightFail(
1329
+ "INVALID_AMOUNT",
1330
+ `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${input.asset}. Got ${input.amount}.`
1331
+ );
1332
+ }
1333
+ const addressCheck = checkSuiAddress(input.to);
1334
+ if (!addressCheck.valid) return addressCheck;
1335
+ return PREFLIGHT_OK;
1336
+ }
1243
1337
  async function buildSendTx({
1244
1338
  client,
1245
1339
  address,
@@ -1247,17 +1341,11 @@ async function buildSendTx({
1247
1341
  amount,
1248
1342
  asset
1249
1343
  }) {
1250
- assertAllowedAsset("send", asset);
1344
+ const pf = preflightSend({ to, amount, asset });
1345
+ if (!pf.valid) throw new T2000Error(pf.code, pf.error);
1251
1346
  const recipient = validateAddress(to);
1252
1347
  const assetInfo = SUPPORTED_ASSETS[asset];
1253
1348
  if (!assetInfo) throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
1254
- if (amount <= 0) throw new T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
1255
- if ((asset === "USDC" || asset === "USDsui") && amount < GASLESS_MIN_STABLE_AMOUNT) {
1256
- throw new T2000Error(
1257
- "INVALID_AMOUNT",
1258
- `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${amount}.`
1259
- );
1260
- }
1261
1349
  const rawAmount = displayToRaw(amount, assetInfo.decimals);
1262
1350
  const tx = new Transaction();
1263
1351
  tx.setSender(address);
@@ -1552,7 +1640,7 @@ var TX_NODE_FRAGMENT = `
1552
1640
  }
1553
1641
  `;
1554
1642
  var HISTORY_QUERY = `query History($address: SuiAddress!, $last: Int!) {
1555
- transactions(last: $last, filter: { sentAddress: $address }) {
1643
+ transactions(last: $last, filter: { affectedAddress: $address }) {
1556
1644
  nodes {${TX_NODE_FRAGMENT}}
1557
1645
  }
1558
1646
  }`;
@@ -1679,7 +1767,12 @@ function extractCommands(txBlock) {
1679
1767
  init_token_registry();
1680
1768
 
1681
1769
  // src/utils/suins.ts
1682
- var SUI_MAINNET_URL = "https://fullnode.mainnet.sui.io:443";
1770
+ var RESOLVE_NAME_QUERY = `query ResolveSuins($name: String!) {
1771
+ address(name: $name) { address }
1772
+ }`;
1773
+ var REVERSE_NAME_QUERY = `query ReverseSuins($address: SuiAddress!) {
1774
+ address(address: $address) { defaultNameRecord { domain } }
1775
+ }`;
1683
1776
  var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/i;
1684
1777
  var SUI_ADDRESS_STRICT_REGEX = /^0x[a-fA-F0-9]{64}$/i;
1685
1778
  var SUINS_NAME_REGEX = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.sui$/;
@@ -1717,76 +1810,45 @@ async function resolveSuinsViaRpc(rawName, ctx = {}) {
1717
1810
  if (!SUINS_NAME_REGEX.test(name)) {
1718
1811
  throw new InvalidAddressError(rawName);
1719
1812
  }
1720
- const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
1813
+ const gql = getSuiGraphQLClient();
1721
1814
  let res;
1722
1815
  try {
1723
- res = await fetch(url, {
1724
- method: "POST",
1725
- headers: { "Content-Type": "application/json" },
1726
- body: JSON.stringify({
1727
- jsonrpc: "2.0",
1728
- id: 1,
1729
- method: "suix_resolveNameServiceAddress",
1730
- params: [name]
1731
- }),
1732
- signal: ctx.signal ?? AbortSignal.timeout(8e3)
1816
+ res = await gql.query({
1817
+ query: RESOLVE_NAME_QUERY,
1818
+ variables: { name },
1819
+ signal: ctx.signal
1733
1820
  });
1734
1821
  } catch (err) {
1735
1822
  const msg = err instanceof Error ? err.message : String(err);
1736
1823
  throw new SuinsRpcError(name, msg);
1737
1824
  }
1738
- if (!res.ok) {
1739
- throw new SuinsRpcError(name, `HTTP ${res.status}`);
1740
- }
1741
- let body;
1742
- try {
1743
- body = await res.json();
1744
- } catch (err) {
1745
- const msg = err instanceof Error ? err.message : String(err);
1746
- throw new SuinsRpcError(name, `JSON parse failed: ${msg}`);
1747
- }
1748
- if (body.error) {
1749
- throw new SuinsRpcError(name, body.error.message);
1825
+ if (res.errors?.length) {
1826
+ throw new SuinsRpcError(name, res.errors.map((e) => e.message ?? "unknown error").join("; "));
1750
1827
  }
1751
- return body.result ?? null;
1828
+ return res.data?.address?.address ?? null;
1752
1829
  }
1753
1830
  async function resolveAddressToSuinsViaRpc(rawAddress, ctx = {}) {
1754
1831
  const address = rawAddress.trim().toLowerCase();
1755
1832
  if (!SUI_ADDRESS_REGEX.test(address)) {
1756
1833
  throw new InvalidAddressError(rawAddress);
1757
1834
  }
1758
- const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
1835
+ const gql = getSuiGraphQLClient();
1759
1836
  let res;
1760
1837
  try {
1761
- res = await fetch(url, {
1762
- method: "POST",
1763
- headers: { "Content-Type": "application/json" },
1764
- body: JSON.stringify({
1765
- jsonrpc: "2.0",
1766
- id: 1,
1767
- method: "suix_resolveNameServiceNames",
1768
- params: [address]
1769
- }),
1770
- signal: ctx.signal ?? AbortSignal.timeout(8e3)
1838
+ res = await gql.query({
1839
+ query: REVERSE_NAME_QUERY,
1840
+ variables: { address },
1841
+ signal: ctx.signal
1771
1842
  });
1772
1843
  } catch (err) {
1773
1844
  const msg = err instanceof Error ? err.message : String(err);
1774
1845
  throw new SuinsRpcError(address, msg);
1775
1846
  }
1776
- if (!res.ok) {
1777
- throw new SuinsRpcError(address, `HTTP ${res.status}`);
1778
- }
1779
- let body;
1780
- try {
1781
- body = await res.json();
1782
- } catch (err) {
1783
- const msg = err instanceof Error ? err.message : String(err);
1784
- throw new SuinsRpcError(address, `JSON parse failed: ${msg}`);
1785
- }
1786
- if (body.error) {
1787
- throw new SuinsRpcError(address, body.error.message);
1847
+ if (res.errors?.length) {
1848
+ throw new SuinsRpcError(address, res.errors.map((e) => e.message ?? "unknown error").join("; "));
1788
1849
  }
1789
- return body.result?.data ?? [];
1850
+ const domain = res.data?.address?.defaultNameRecord?.domain;
1851
+ return domain ? [domain] : [];
1790
1852
  }
1791
1853
  async function normalizeAddressInput(value, ctx = {}) {
1792
1854
  const trimmed = value.trim();
@@ -2079,7 +2141,9 @@ var T2000 = class _T2000 extends EventEmitter {
2079
2141
  amountUsd: approxUsdValue(params.from, params.amount) ?? 0,
2080
2142
  force: params.force
2081
2143
  });
2082
- const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
2144
+ const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2, preflightSwap: preflightSwap2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
2145
+ const pf = preflightSwap2({ from: params.from, to: params.to, amount: params.amount });
2146
+ if (!pf.valid) throw new T2000Error(pf.code, pf.error);
2083
2147
  const fromType = resolveTokenType2(params.from);
2084
2148
  const toType = resolveTokenType2(params.to);
2085
2149
  if (!fromType) throw new T2000Error("ASSET_NOT_SUPPORTED", `Unknown token: ${params.from}. Provide the full coin type.`);
@@ -2809,6 +2873,9 @@ function displayHandle(label) {
2809
2873
  return `${label}@${parentDisplay}`;
2810
2874
  }
2811
2875
 
2812
- export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, DEFAULT_GRPC_URL, DEFAULT_NETWORK, ETH_TYPE, GASLESS_MIN_STABLE_AMOUNT, GASLESS_STABLE_TYPES, GAS_RESERVE_MIN, IKA_TYPE, InvalidAddressError, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, LimitEnforcer, LimitExceededError, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, OPERATION_ASSETS, OVERLAY_FEE_RATE, SENDABLE_ASSETS, SPONSORED_PYTH_DEPENDENT_PROVIDERS, STABLE_ASSETS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SuinsNotRegisteredError, SuinsRpcError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, WAL_TYPE, WBTC_TYPE, WRITE_APPENDER_REGISTRY, ZkLoginSigner, addSendToTx, addSwapToTx, approxUsdValue, assertAllowedAsset, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, buildSendTx, buildSwapTx, classifyAction, classifyLabel, classifyTransaction, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, deserializeCetusRoute, displayHandle, executeTx, exportPrivateKey, extractAllUserLegs, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, fetchAllCoins, findSwapRoute, formatAssetAmount, formatSui, formatUsd, fullHandle, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getLimits, getSponsoredSwapProviders, getSuiClient, getSuiGrpcClient, getSwapQuote, hasLimits, isAllowedAsset, isCetusRouteFresh, isInRegistry, keypairFromPrivateKey, loadKey, looksLikeSuiNs, mapMoveAbortCode, mapWalletError, mistToSui, normalizeAddressInput, normalizeAsset, normalizeCoinType, parseSuiRpcTx, payWithMpp, queryHistory, queryTransaction, rawToStable, rawToUsdc, readLimitsFile, recordDailySpend, refineLendingLabel, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, resolveSymbol, resolveTokenType, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, serializeCetusRoute, setLimits, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, validateLabel, verifyCetusRouteCoinMatch, walletExists, writeLimitsFile };
2876
+ // src/index.ts
2877
+ init_preflight();
2878
+
2879
+ export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, CETUS_USDC_SUI_POOL, CLOCK_ID, COIN_REGISTRY, DEFAULT_GRPC_URL, DEFAULT_NETWORK, ETH_TYPE, GASLESS_MIN_STABLE_AMOUNT, GASLESS_STABLE_TYPES, GAS_RESERVE_MIN, IKA_TYPE, InvalidAddressError, KNOWN_TARGETS, KeypairSigner, LABEL_PATTERNS, LOFI_TYPE, LimitEnforcer, LimitExceededError, MANIFEST_TYPE, MIST_PER_SUI, NAVX_TYPE, OPERATION_ASSETS, OVERLAY_FEE_RATE, PREFLIGHT_MAX_AMOUNT, PREFLIGHT_OK, SENDABLE_ASSETS, SPONSORED_PYTH_DEPENDENT_PROVIDERS, STABLE_ASSETS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SUI_DECIMALS, SUI_TYPE, SUPPORTED_ASSETS, SuinsNotRegisteredError, SuinsRpcError, T2000, T2000Error, T2000_OVERLAY_FEE_WALLET, TOKEN_MAP, USDC_DECIMALS, USDC_TYPE, USDE_TYPE, USDSUI_TYPE, USDT_TYPE, WAL_TYPE, WBTC_TYPE, WRITE_APPENDER_REGISTRY, ZkLoginSigner, addSendToTx, addSwapToTx, approxUsdValue, assertAllowedAsset, assertLimitConfig, buildAddLeafTx, buildRevokeLeafTx, buildSendTx, buildSwapTx, checkPositiveAmount, checkSuiAddress, classifyAction, classifyLabel, classifyTransaction, clearLimits, composeTx, dailySpentToday, deriveAllowedAddressesFromPtb, deserializeCetusRoute, displayHandle, executeTx, exportPrivateKey, extractAllUserLegs, extractTransferDetails, extractTxCommands, extractTxSender, fallbackLabel, fetchAllCoins, findSwapRoute, formatAssetAmount, formatSui, formatUsd, fullHandle, generateKeypair, getAddress, getCoinMeta, getDecimals, getDecimalsForCoinType, getLimits, getSponsoredSwapProviders, getSuiClient, getSuiGrpcClient, getSwapQuote, hasLimits, isAllowedAsset, isCetusRouteFresh, isInRegistry, keypairFromPrivateKey, loadKey, looksLikeSuiNs, mapMoveAbortCode, mapWalletError, mistToSui, normalizeAddressInput, normalizeAsset, normalizeCoinType, parseSuiRpcTx, payWithMpp, preflightFail, preflightPay, preflightSend, preflightSwap, queryBalance, queryHistory, queryTransaction, rawToStable, rawToUsdc, readLimitsFile, recordDailySpend, refineLendingLabel, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, resolveSymbol, resolveTokenType, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, serializeCetusRoute, setLimits, simulateTransaction, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, validateLabel, verifyCetusRouteCoinMatch, walletExists, writeLimitsFile };
2813
2880
  //# sourceMappingURL=index.js.map
2814
2881
  //# sourceMappingURL=index.js.map