@t2000/sdk 2.18.1 → 2.19.1

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;
@@ -6301,6 +6435,7 @@ var ContactManager = class {
6301
6435
  const existed = key in this.contacts;
6302
6436
  this.contacts[key] = { name, address: normalized };
6303
6437
  this.save();
6438
+ warnContactsDeprecation();
6304
6439
  return { action: existed ? "updated" : "added" };
6305
6440
  }
6306
6441
  remove(name) {
@@ -6325,12 +6460,15 @@ var ContactManager = class {
6325
6460
  }
6326
6461
  const contact = this.contacts[nameOrAddress.toLowerCase()];
6327
6462
  if (contact) {
6463
+ warnContactsDeprecation();
6328
6464
  return { address: contact.address, contactName: contact.name };
6329
6465
  }
6330
6466
  throw new exports.T2000Error(
6331
6467
  "CONTACT_NOT_FOUND",
6332
6468
  `"${nameOrAddress}" is not a valid Sui address or saved contact.
6333
- Add it: t2000 contacts add ${nameOrAddress} 0x...`
6469
+ Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
6470
+ or paste the full Sui address (0x... 64 hex characters).
6471
+ Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
6334
6472
  );
6335
6473
  }
6336
6474
  validateName(name) {
@@ -6700,7 +6838,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6700
6838
  if (!(asset in SUPPORTED_ASSETS)) {
6701
6839
  throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
6702
6840
  }
6703
- const resolved = this.contacts.resolve(params.to);
6841
+ const resolved = await this.resolveRecipient(params.to);
6704
6842
  const sendAmount = params.amount;
6705
6843
  const sendTo = resolved.address;
6706
6844
  const gasResult = await executeTx(
@@ -6717,11 +6855,63 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6717
6855
  amount: sendAmount,
6718
6856
  to: resolved.address,
6719
6857
  contactName: resolved.contactName,
6858
+ suinsName: resolved.suinsName,
6720
6859
  gasCost: gasResult.gasCostSui,
6721
6860
  gasCostUnit: "SUI",
6722
6861
  balance
6723
6862
  };
6724
6863
  }
6864
+ /**
6865
+ * [S.279 / CLI-CONTACTS-CLEANUP — 2026-05-23] Resolve a recipient
6866
+ * string into a canonical 0x address, trying each shape in priority
6867
+ * order:
6868
+ * 1. **hex** — `0x…` is used directly (no RPC round-trip).
6869
+ * 2. **SuiNS** — `alex.sui` resolves via `suix_resolveNameServiceAddress`.
6870
+ * 3. **saved contact** — `ContactManager.resolve()` falls back to the
6871
+ * legacy `~/.t2000/contacts.json` alias map. Deprecated; the
6872
+ * manager surfaces a one-time `console.warn` when this path fires.
6873
+ *
6874
+ * Returns `{ address, suinsName?, contactName? }` so `send()` can stamp
6875
+ * the name source on the receipt without re-resolving.
6876
+ *
6877
+ * Throws `T2000Error('SUINS_NOT_REGISTERED', …)` for well-formed but
6878
+ * unregistered SuiNS names (vs. propagating the engine-style
6879
+ * `SuinsNotRegisteredError` — keeps the SDK's error surface
6880
+ * `T2000Error`-only, consistent with every other write helper).
6881
+ *
6882
+ * [S.279.1 / 2026-05-23 — patch v2.19.1] Promoted from `private` to
6883
+ * `public` so MCP's `t2000_send` dryRun preview path can share the
6884
+ * same resolution logic as the live execute path. Pre-2.19.1 the
6885
+ * MCP dryRun called `agent.contacts.resolve(to)` directly, which
6886
+ * rejected SuiNS names — preview-then-execute flows broke for
6887
+ * `alex.sui`-style recipients. SSOT: never let preview and execute
6888
+ * resolve the same input differently.
6889
+ */
6890
+ async resolveRecipient(input) {
6891
+ const trimmed = input.trim();
6892
+ if (SUI_ADDRESS_REGEX.test(trimmed)) {
6893
+ return { address: trimmed.toLowerCase() };
6894
+ }
6895
+ if (looksLikeSuiNs(trimmed)) {
6896
+ try {
6897
+ const name = trimmed.toLowerCase();
6898
+ const address = await resolveSuinsViaRpc(name);
6899
+ if (!address) {
6900
+ throw new SuinsNotRegisteredError(name);
6901
+ }
6902
+ return { address: address.toLowerCase(), suinsName: name };
6903
+ } catch (err) {
6904
+ if (err instanceof SuinsNotRegisteredError) {
6905
+ throw new exports.T2000Error(
6906
+ "SUINS_NOT_REGISTERED",
6907
+ err.message
6908
+ );
6909
+ }
6910
+ throw err;
6911
+ }
6912
+ }
6913
+ return this.contacts.resolve(input);
6914
+ }
6725
6915
  async balance() {
6726
6916
  const bal = await queryBalance(this.client, this._address);
6727
6917
  let chainTotal = bal.available + bal.gasReserve.usdEquiv;
@@ -8398,6 +8588,7 @@ exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
8398
8588
  exports.GAS_RESERVE_MIN = GAS_RESERVE_MIN;
8399
8589
  exports.HF_CRITICAL_THRESHOLD = HF_CRITICAL_THRESHOLD;
8400
8590
  exports.HF_WARN_THRESHOLD = HF_WARN_THRESHOLD;
8591
+ exports.InvalidAddressError = InvalidAddressError;
8401
8592
  exports.KNOWN_TARGETS = KNOWN_TARGETS;
8402
8593
  exports.KeypairSigner = KeypairSigner;
8403
8594
  exports.LABEL_PATTERNS = LABEL_PATTERNS;
@@ -8409,10 +8600,15 @@ exports.ProtocolRegistry = ProtocolRegistry;
8409
8600
  exports.SAVE_FEE_BPS = SAVE_FEE_BPS;
8410
8601
  exports.SPONSORED_PYTH_DEPENDENT_PROVIDERS = SPONSORED_PYTH_DEPENDENT_PROVIDERS;
8411
8602
  exports.STABLE_ASSETS = STABLE_ASSETS;
8603
+ exports.SUINS_NAME_REGEX = SUINS_NAME_REGEX;
8604
+ exports.SUI_ADDRESS_REGEX = SUI_ADDRESS_REGEX;
8605
+ exports.SUI_ADDRESS_STRICT_REGEX = SUI_ADDRESS_STRICT_REGEX;
8412
8606
  exports.SUI_DECIMALS = SUI_DECIMALS;
8413
8607
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
8414
8608
  exports.SafeguardEnforcer = SafeguardEnforcer;
8415
8609
  exports.SafeguardError = SafeguardError;
8610
+ exports.SuinsNotRegisteredError = SuinsNotRegisteredError;
8611
+ exports.SuinsRpcError = SuinsRpcError;
8416
8612
  exports.T2000 = T2000;
8417
8613
  exports.T2000_OVERLAY_FEE_WALLET = T2000_OVERLAY_FEE_WALLET;
8418
8614
  exports.USDC_DECIMALS = USDC_DECIMALS;
@@ -8476,10 +8672,12 @@ exports.isTier1 = isTier1;
8476
8672
  exports.isTier2 = isTier2;
8477
8673
  exports.keypairFromPrivateKey = keypairFromPrivateKey;
8478
8674
  exports.loadKey = loadKey;
8675
+ exports.looksLikeSuiNs = looksLikeSuiNs;
8479
8676
  exports.mapMoveAbortCode = mapMoveAbortCode;
8480
8677
  exports.mapWalletError = mapWalletError;
8481
8678
  exports.mistToSui = mistToSui;
8482
8679
  exports.naviDescriptor = naviDescriptor;
8680
+ exports.normalizeAddressInput = normalizeAddressInput;
8483
8681
  exports.normalizeAsset = normalizeAsset;
8484
8682
  exports.normalizeCoinType = normalizeCoinType;
8485
8683
  exports.parseSuiRpcTx = parseSuiRpcTx;
@@ -8488,6 +8686,8 @@ exports.queryTransaction = queryTransaction;
8488
8686
  exports.rawToStable = rawToStable;
8489
8687
  exports.rawToUsdc = rawToUsdc;
8490
8688
  exports.refineLendingLabel = refineLendingLabel;
8689
+ exports.resolveAddressToSuinsViaRpc = resolveAddressToSuinsViaRpc;
8690
+ exports.resolveSuinsViaRpc = resolveSuinsViaRpc;
8491
8691
  exports.resolveSymbol = resolveSymbol;
8492
8692
  exports.resolveTokenType = resolveTokenType;
8493
8693
  exports.saveKey = saveKey;