@t2000/sdk 2.18.1 → 2.19.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
@@ -1805,6 +1805,132 @@ function extractCommands(txBlock) {
1805
1805
  // src/t2000.ts
1806
1806
  init_token_registry();
1807
1807
 
1808
+ // src/utils/suins.ts
1809
+ var SUI_MAINNET_URL = "https://fullnode.mainnet.sui.io:443";
1810
+ var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/i;
1811
+ var SUI_ADDRESS_STRICT_REGEX = /^0x[a-fA-F0-9]{64}$/i;
1812
+ var SUINS_NAME_REGEX = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.sui$/;
1813
+ var InvalidAddressError = class extends Error {
1814
+ constructor(raw) {
1815
+ super(
1816
+ `"${raw}" isn't a valid Sui address or SuiNS name. Pass a 0x-prefixed hex address (e.g. 0x40cd\u20263e62) or a SuiNS name ending in .sui (e.g. alex.sui).`
1817
+ );
1818
+ this.raw = raw;
1819
+ this.name = "InvalidAddressError";
1820
+ }
1821
+ };
1822
+ var SuinsNotRegisteredError = class extends Error {
1823
+ constructor(name_) {
1824
+ super(
1825
+ `"${name_}" isn't a registered SuiNS name. Double-check the spelling, or paste the full Sui address (0x\u2026 64 hex characters).`
1826
+ );
1827
+ this.name_ = name_;
1828
+ this.name = "SuinsNotRegisteredError";
1829
+ }
1830
+ };
1831
+ var SuinsRpcError = class extends Error {
1832
+ constructor(name_, detail) {
1833
+ super(`SuiNS lookup failed for "${name_}" (${detail}). Try again, or paste the full Sui address.`);
1834
+ this.name_ = name_;
1835
+ this.name = "SuinsRpcError";
1836
+ }
1837
+ };
1838
+ function looksLikeSuiNs(value) {
1839
+ if (!value) return false;
1840
+ return SUINS_NAME_REGEX.test(value.trim().toLowerCase());
1841
+ }
1842
+ async function resolveSuinsViaRpc(rawName, ctx = {}) {
1843
+ const name = rawName.trim().toLowerCase();
1844
+ if (!SUINS_NAME_REGEX.test(name)) {
1845
+ throw new InvalidAddressError(rawName);
1846
+ }
1847
+ const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
1848
+ let res;
1849
+ try {
1850
+ res = await fetch(url, {
1851
+ method: "POST",
1852
+ headers: { "Content-Type": "application/json" },
1853
+ body: JSON.stringify({
1854
+ jsonrpc: "2.0",
1855
+ id: 1,
1856
+ method: "suix_resolveNameServiceAddress",
1857
+ params: [name]
1858
+ }),
1859
+ signal: ctx.signal ?? AbortSignal.timeout(8e3)
1860
+ });
1861
+ } catch (err) {
1862
+ const msg = err instanceof Error ? err.message : String(err);
1863
+ throw new SuinsRpcError(name, msg);
1864
+ }
1865
+ if (!res.ok) {
1866
+ throw new SuinsRpcError(name, `HTTP ${res.status}`);
1867
+ }
1868
+ let body;
1869
+ try {
1870
+ body = await res.json();
1871
+ } catch (err) {
1872
+ const msg = err instanceof Error ? err.message : String(err);
1873
+ throw new SuinsRpcError(name, `JSON parse failed: ${msg}`);
1874
+ }
1875
+ if (body.error) {
1876
+ throw new SuinsRpcError(name, body.error.message);
1877
+ }
1878
+ return body.result ?? null;
1879
+ }
1880
+ async function resolveAddressToSuinsViaRpc(rawAddress, ctx = {}) {
1881
+ const address = rawAddress.trim().toLowerCase();
1882
+ if (!SUI_ADDRESS_REGEX.test(address)) {
1883
+ throw new InvalidAddressError(rawAddress);
1884
+ }
1885
+ const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
1886
+ let res;
1887
+ try {
1888
+ res = await fetch(url, {
1889
+ method: "POST",
1890
+ headers: { "Content-Type": "application/json" },
1891
+ body: JSON.stringify({
1892
+ jsonrpc: "2.0",
1893
+ id: 1,
1894
+ method: "suix_resolveNameServiceNames",
1895
+ params: [address]
1896
+ }),
1897
+ signal: ctx.signal ?? AbortSignal.timeout(8e3)
1898
+ });
1899
+ } catch (err) {
1900
+ const msg = err instanceof Error ? err.message : String(err);
1901
+ throw new SuinsRpcError(address, msg);
1902
+ }
1903
+ if (!res.ok) {
1904
+ throw new SuinsRpcError(address, `HTTP ${res.status}`);
1905
+ }
1906
+ let body;
1907
+ try {
1908
+ body = await res.json();
1909
+ } catch (err) {
1910
+ const msg = err instanceof Error ? err.message : String(err);
1911
+ throw new SuinsRpcError(address, `JSON parse failed: ${msg}`);
1912
+ }
1913
+ if (body.error) {
1914
+ throw new SuinsRpcError(address, body.error.message);
1915
+ }
1916
+ return body.result?.data ?? [];
1917
+ }
1918
+ async function normalizeAddressInput(value, ctx = {}) {
1919
+ const trimmed = value.trim();
1920
+ if (SUI_ADDRESS_REGEX.test(trimmed)) {
1921
+ return { address: trimmed.toLowerCase(), suinsName: null, raw: value };
1922
+ }
1923
+ if (looksLikeSuiNs(trimmed)) {
1924
+ const name = trimmed.toLowerCase();
1925
+ const address = await resolveSuinsViaRpc(name, ctx);
1926
+ if (!address) {
1927
+ throw new SuinsNotRegisteredError(name);
1928
+ }
1929
+ return { address: address.toLowerCase(), suinsName: name, raw: value };
1930
+ }
1931
+ throw new InvalidAddressError(value);
1932
+ }
1933
+
1808
1934
  // ../../node_modules/.pnpm/@mysten+bcs@2.0.2/node_modules/@mysten/bcs/dist/uleb.mjs
1809
1935
  function ulebEncode(num) {
1810
1936
  let bigNum = BigInt(num);
@@ -6272,6 +6398,14 @@ var SafeguardEnforcer = class {
6272
6398
  };
6273
6399
  init_errors();
6274
6400
  var RESERVED_NAMES = /* @__PURE__ */ new Set(["to", "all", "address"]);
6401
+ var deprecationWarned = false;
6402
+ function warnContactsDeprecation() {
6403
+ if (deprecationWarned) return;
6404
+ deprecationWarned = true;
6405
+ console.warn(
6406
+ "[t2000] DEPRECATION: ~/.t2000/contacts.json alias resolution is deprecated and will be removed in the next major release. Use SuiNS names instead (e.g. `t2000 send alex.sui 10 USDC`). Register a SuiNS name at https://suins.io if you need a memorable handle."
6407
+ );
6408
+ }
6275
6409
  var ContactManager = class {
6276
6410
  contacts = {};
6277
6411
  filePath;
@@ -6325,12 +6459,15 @@ var ContactManager = class {
6325
6459
  }
6326
6460
  const contact = this.contacts[nameOrAddress.toLowerCase()];
6327
6461
  if (contact) {
6462
+ warnContactsDeprecation();
6328
6463
  return { address: contact.address, contactName: contact.name };
6329
6464
  }
6330
6465
  throw new exports.T2000Error(
6331
6466
  "CONTACT_NOT_FOUND",
6332
6467
  `"${nameOrAddress}" is not a valid Sui address or saved contact.
6333
- Add it: t2000 contacts add ${nameOrAddress} 0x...`
6468
+ Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
6469
+ or paste the full Sui address (0x... 64 hex characters).
6470
+ Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
6334
6471
  );
6335
6472
  }
6336
6473
  validateName(name) {
@@ -6700,7 +6837,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6700
6837
  if (!(asset in SUPPORTED_ASSETS)) {
6701
6838
  throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
6702
6839
  }
6703
- const resolved = this.contacts.resolve(params.to);
6840
+ const resolved = await this.resolveRecipient(params.to);
6704
6841
  const sendAmount = params.amount;
6705
6842
  const sendTo = resolved.address;
6706
6843
  const gasResult = await executeTx(
@@ -6717,11 +6854,55 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6717
6854
  amount: sendAmount,
6718
6855
  to: resolved.address,
6719
6856
  contactName: resolved.contactName,
6857
+ suinsName: resolved.suinsName,
6720
6858
  gasCost: gasResult.gasCostSui,
6721
6859
  gasCostUnit: "SUI",
6722
6860
  balance
6723
6861
  };
6724
6862
  }
6863
+ /**
6864
+ * [S.279 / CLI-CONTACTS-CLEANUP — 2026-05-23] Resolve a recipient
6865
+ * string into a canonical 0x address, trying each shape in priority
6866
+ * order:
6867
+ * 1. **hex** — `0x…` is used directly (no RPC round-trip).
6868
+ * 2. **SuiNS** — `alex.sui` resolves via `suix_resolveNameServiceAddress`.
6869
+ * 3. **saved contact** — `ContactManager.resolve()` falls back to the
6870
+ * legacy `~/.t2000/contacts.json` alias map. Deprecated; the
6871
+ * manager surfaces a one-time `console.warn` when this path fires.
6872
+ *
6873
+ * Returns `{ address, suinsName?, contactName? }` so `send()` can stamp
6874
+ * the name source on the receipt without re-resolving.
6875
+ *
6876
+ * Throws `T2000Error('SUINS_NOT_REGISTERED', …)` for well-formed but
6877
+ * unregistered SuiNS names (vs. propagating the engine-style
6878
+ * `SuinsNotRegisteredError` — keeps the SDK's error surface
6879
+ * `T2000Error`-only, consistent with every other write helper).
6880
+ */
6881
+ async resolveRecipient(input) {
6882
+ const trimmed = input.trim();
6883
+ if (SUI_ADDRESS_REGEX.test(trimmed)) {
6884
+ return { address: trimmed.toLowerCase() };
6885
+ }
6886
+ if (looksLikeSuiNs(trimmed)) {
6887
+ try {
6888
+ const name = trimmed.toLowerCase();
6889
+ const address = await resolveSuinsViaRpc(name);
6890
+ if (!address) {
6891
+ throw new SuinsNotRegisteredError(name);
6892
+ }
6893
+ return { address: address.toLowerCase(), suinsName: name };
6894
+ } catch (err) {
6895
+ if (err instanceof SuinsNotRegisteredError) {
6896
+ throw new exports.T2000Error(
6897
+ "SUINS_NOT_REGISTERED",
6898
+ err.message
6899
+ );
6900
+ }
6901
+ throw err;
6902
+ }
6903
+ }
6904
+ return this.contacts.resolve(input);
6905
+ }
6725
6906
  async balance() {
6726
6907
  const bal = await queryBalance(this.client, this._address);
6727
6908
  let chainTotal = bal.available + bal.gasReserve.usdEquiv;
@@ -8398,6 +8579,7 @@ exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
8398
8579
  exports.GAS_RESERVE_MIN = GAS_RESERVE_MIN;
8399
8580
  exports.HF_CRITICAL_THRESHOLD = HF_CRITICAL_THRESHOLD;
8400
8581
  exports.HF_WARN_THRESHOLD = HF_WARN_THRESHOLD;
8582
+ exports.InvalidAddressError = InvalidAddressError;
8401
8583
  exports.KNOWN_TARGETS = KNOWN_TARGETS;
8402
8584
  exports.KeypairSigner = KeypairSigner;
8403
8585
  exports.LABEL_PATTERNS = LABEL_PATTERNS;
@@ -8409,10 +8591,15 @@ exports.ProtocolRegistry = ProtocolRegistry;
8409
8591
  exports.SAVE_FEE_BPS = SAVE_FEE_BPS;
8410
8592
  exports.SPONSORED_PYTH_DEPENDENT_PROVIDERS = SPONSORED_PYTH_DEPENDENT_PROVIDERS;
8411
8593
  exports.STABLE_ASSETS = STABLE_ASSETS;
8594
+ exports.SUINS_NAME_REGEX = SUINS_NAME_REGEX;
8595
+ exports.SUI_ADDRESS_REGEX = SUI_ADDRESS_REGEX;
8596
+ exports.SUI_ADDRESS_STRICT_REGEX = SUI_ADDRESS_STRICT_REGEX;
8412
8597
  exports.SUI_DECIMALS = SUI_DECIMALS;
8413
8598
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
8414
8599
  exports.SafeguardEnforcer = SafeguardEnforcer;
8415
8600
  exports.SafeguardError = SafeguardError;
8601
+ exports.SuinsNotRegisteredError = SuinsNotRegisteredError;
8602
+ exports.SuinsRpcError = SuinsRpcError;
8416
8603
  exports.T2000 = T2000;
8417
8604
  exports.T2000_OVERLAY_FEE_WALLET = T2000_OVERLAY_FEE_WALLET;
8418
8605
  exports.USDC_DECIMALS = USDC_DECIMALS;
@@ -8476,10 +8663,12 @@ exports.isTier1 = isTier1;
8476
8663
  exports.isTier2 = isTier2;
8477
8664
  exports.keypairFromPrivateKey = keypairFromPrivateKey;
8478
8665
  exports.loadKey = loadKey;
8666
+ exports.looksLikeSuiNs = looksLikeSuiNs;
8479
8667
  exports.mapMoveAbortCode = mapMoveAbortCode;
8480
8668
  exports.mapWalletError = mapWalletError;
8481
8669
  exports.mistToSui = mistToSui;
8482
8670
  exports.naviDescriptor = naviDescriptor;
8671
+ exports.normalizeAddressInput = normalizeAddressInput;
8483
8672
  exports.normalizeAsset = normalizeAsset;
8484
8673
  exports.normalizeCoinType = normalizeCoinType;
8485
8674
  exports.parseSuiRpcTx = parseSuiRpcTx;
@@ -8488,6 +8677,8 @@ exports.queryTransaction = queryTransaction;
8488
8677
  exports.rawToStable = rawToStable;
8489
8678
  exports.rawToUsdc = rawToUsdc;
8490
8679
  exports.refineLendingLabel = refineLendingLabel;
8680
+ exports.resolveAddressToSuinsViaRpc = resolveAddressToSuinsViaRpc;
8681
+ exports.resolveSuinsViaRpc = resolveSuinsViaRpc;
8491
8682
  exports.resolveSymbol = resolveSymbol;
8492
8683
  exports.resolveTokenType = resolveTokenType;
8493
8684
  exports.saveKey = saveKey;