@t2000/cli 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/README.md CHANGED
@@ -180,14 +180,18 @@ t2000 init
180
180
  |---------|-------------|
181
181
  | `t2000 earn` | Show all earning opportunities — savings yield |
182
182
 
183
- ### Contacts
183
+ ### Contacts (DEPRECATED — sunset in next major)
184
+
185
+ The local `~/.t2000/contacts.json` alias map is being retired. **Use SuiNS instead**: register `your-name.sui` once at https://suins.io and every Sui app — including `t2000 send` — resolves it automatically. The first time a process resolves or writes a legacy contact alias, the CLI prints a one-shot deprecation warning to stderr.
184
186
 
185
187
  | Command | Description |
186
188
  |---------|-------------|
187
- | `t2000 contacts` | List saved contacts |
188
- | `t2000 contacts add <name> <address>` | Save a named contact |
189
+ | `t2000 contacts` | List saved contacts (deprecated) |
190
+ | `t2000 contacts add <name> <address>` | Save a named contact (deprecated; warns once) |
189
191
  | `t2000 contacts remove <name>` | Remove a contact |
190
192
 
193
+ `t2000 send` accepts SuiNS names natively — `t2000 send 10 USDC to alex.sui` works with no setup. Priority order for the recipient argument: `0x` address > SuiNS name > saved contact alias.
194
+
191
195
  ### Safeguards
192
196
 
193
197
  | Command | Description |
@@ -17575,6 +17575,130 @@ function extractCommands(txBlock) {
17575
17575
  return result;
17576
17576
  }
17577
17577
  init_token_registry();
17578
+ var SUI_MAINNET_URL = "https://fullnode.mainnet.sui.io:443";
17579
+ var SUI_ADDRESS_REGEX = /^0x[a-fA-F0-9]{1,64}$/i;
17580
+ var SUI_ADDRESS_STRICT_REGEX = /^0x[a-fA-F0-9]{64}$/i;
17581
+ var SUINS_NAME_REGEX = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.sui$/;
17582
+ var InvalidAddressError = class extends Error {
17583
+ constructor(raw) {
17584
+ super(
17585
+ `"${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).`
17586
+ );
17587
+ this.raw = raw;
17588
+ this.name = "InvalidAddressError";
17589
+ }
17590
+ };
17591
+ var SuinsNotRegisteredError = class extends Error {
17592
+ constructor(name_) {
17593
+ super(
17594
+ `"${name_}" isn't a registered SuiNS name. Double-check the spelling, or paste the full Sui address (0x\u2026 64 hex characters).`
17595
+ );
17596
+ this.name_ = name_;
17597
+ this.name = "SuinsNotRegisteredError";
17598
+ }
17599
+ };
17600
+ var SuinsRpcError = class extends Error {
17601
+ constructor(name_, detail) {
17602
+ super(`SuiNS lookup failed for "${name_}" (${detail}). Try again, or paste the full Sui address.`);
17603
+ this.name_ = name_;
17604
+ this.name = "SuinsRpcError";
17605
+ }
17606
+ };
17607
+ function looksLikeSuiNs(value) {
17608
+ if (!value) return false;
17609
+ return SUINS_NAME_REGEX.test(value.trim().toLowerCase());
17610
+ }
17611
+ async function resolveSuinsViaRpc(rawName, ctx = {}) {
17612
+ const name = rawName.trim().toLowerCase();
17613
+ if (!SUINS_NAME_REGEX.test(name)) {
17614
+ throw new InvalidAddressError(rawName);
17615
+ }
17616
+ const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
17617
+ let res;
17618
+ try {
17619
+ res = await fetch(url, {
17620
+ method: "POST",
17621
+ headers: { "Content-Type": "application/json" },
17622
+ body: JSON.stringify({
17623
+ jsonrpc: "2.0",
17624
+ id: 1,
17625
+ method: "suix_resolveNameServiceAddress",
17626
+ params: [name]
17627
+ }),
17628
+ signal: ctx.signal ?? AbortSignal.timeout(8e3)
17629
+ });
17630
+ } catch (err) {
17631
+ const msg = err instanceof Error ? err.message : String(err);
17632
+ throw new SuinsRpcError(name, msg);
17633
+ }
17634
+ if (!res.ok) {
17635
+ throw new SuinsRpcError(name, `HTTP ${res.status}`);
17636
+ }
17637
+ let body;
17638
+ try {
17639
+ body = await res.json();
17640
+ } catch (err) {
17641
+ const msg = err instanceof Error ? err.message : String(err);
17642
+ throw new SuinsRpcError(name, `JSON parse failed: ${msg}`);
17643
+ }
17644
+ if (body.error) {
17645
+ throw new SuinsRpcError(name, body.error.message);
17646
+ }
17647
+ return body.result ?? null;
17648
+ }
17649
+ async function resolveAddressToSuinsViaRpc(rawAddress, ctx = {}) {
17650
+ const address = rawAddress.trim().toLowerCase();
17651
+ if (!SUI_ADDRESS_REGEX.test(address)) {
17652
+ throw new InvalidAddressError(rawAddress);
17653
+ }
17654
+ const url = ctx.suiRpcUrl || SUI_MAINNET_URL;
17655
+ let res;
17656
+ try {
17657
+ res = await fetch(url, {
17658
+ method: "POST",
17659
+ headers: { "Content-Type": "application/json" },
17660
+ body: JSON.stringify({
17661
+ jsonrpc: "2.0",
17662
+ id: 1,
17663
+ method: "suix_resolveNameServiceNames",
17664
+ params: [address]
17665
+ }),
17666
+ signal: ctx.signal ?? AbortSignal.timeout(8e3)
17667
+ });
17668
+ } catch (err) {
17669
+ const msg = err instanceof Error ? err.message : String(err);
17670
+ throw new SuinsRpcError(address, msg);
17671
+ }
17672
+ if (!res.ok) {
17673
+ throw new SuinsRpcError(address, `HTTP ${res.status}`);
17674
+ }
17675
+ let body;
17676
+ try {
17677
+ body = await res.json();
17678
+ } catch (err) {
17679
+ const msg = err instanceof Error ? err.message : String(err);
17680
+ throw new SuinsRpcError(address, `JSON parse failed: ${msg}`);
17681
+ }
17682
+ if (body.error) {
17683
+ throw new SuinsRpcError(address, body.error.message);
17684
+ }
17685
+ return body.result?.data ?? [];
17686
+ }
17687
+ async function normalizeAddressInput(value, ctx = {}) {
17688
+ const trimmed = value.trim();
17689
+ if (SUI_ADDRESS_REGEX.test(trimmed)) {
17690
+ return { address: trimmed.toLowerCase(), suinsName: null, raw: value };
17691
+ }
17692
+ if (looksLikeSuiNs(trimmed)) {
17693
+ const name = trimmed.toLowerCase();
17694
+ const address = await resolveSuinsViaRpc(name, ctx);
17695
+ if (!address) {
17696
+ throw new SuinsNotRegisteredError(name);
17697
+ }
17698
+ return { address: address.toLowerCase(), suinsName: name, raw: value };
17699
+ }
17700
+ throw new InvalidAddressError(value);
17701
+ }
17578
17702
  function ulebEncode(num) {
17579
17703
  let bigNum = BigInt(num);
17580
17704
  const arr = [];
@@ -21999,6 +22123,14 @@ var SafeguardEnforcer = class {
21999
22123
  };
22000
22124
  init_errors();
22001
22125
  var RESERVED_NAMES = /* @__PURE__ */ new Set(["to", "all", "address"]);
22126
+ var deprecationWarned = false;
22127
+ function warnContactsDeprecation() {
22128
+ if (deprecationWarned) return;
22129
+ deprecationWarned = true;
22130
+ console.warn(
22131
+ "[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."
22132
+ );
22133
+ }
22002
22134
  var ContactManager = class {
22003
22135
  contacts = {};
22004
22136
  filePath;
@@ -22028,6 +22160,7 @@ var ContactManager = class {
22028
22160
  const existed = key in this.contacts;
22029
22161
  this.contacts[key] = { name, address: normalized };
22030
22162
  this.save();
22163
+ warnContactsDeprecation();
22031
22164
  return { action: existed ? "updated" : "added" };
22032
22165
  }
22033
22166
  remove(name) {
@@ -22052,12 +22185,15 @@ var ContactManager = class {
22052
22185
  }
22053
22186
  const contact = this.contacts[nameOrAddress.toLowerCase()];
22054
22187
  if (contact) {
22188
+ warnContactsDeprecation();
22055
22189
  return { address: contact.address, contactName: contact.name };
22056
22190
  }
22057
22191
  throw new T2000Error(
22058
22192
  "CONTACT_NOT_FOUND",
22059
22193
  `"${nameOrAddress}" is not a valid Sui address or saved contact.
22060
- Add it: t2000 contacts add ${nameOrAddress} 0x...`
22194
+ Use a SuiNS name (e.g. alex.sui \u2014 register at https://suins.io)
22195
+ or paste the full Sui address (0x... 64 hex characters).
22196
+ Legacy contact aliases: \`t2000 contacts add ${nameOrAddress} 0x...\` (deprecated).`
22061
22197
  );
22062
22198
  }
22063
22199
  validateName(name) {
@@ -22427,7 +22563,7 @@ var T2000 = class _T2000 extends import_index.default {
22427
22563
  if (!(asset in SUPPORTED_ASSETS)) {
22428
22564
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
22429
22565
  }
22430
- const resolved = this.contacts.resolve(params.to);
22566
+ const resolved = await this.resolveRecipient(params.to);
22431
22567
  const sendAmount = params.amount;
22432
22568
  const sendTo = resolved.address;
22433
22569
  const gasResult = await executeTx(
@@ -22444,11 +22580,63 @@ var T2000 = class _T2000 extends import_index.default {
22444
22580
  amount: sendAmount,
22445
22581
  to: resolved.address,
22446
22582
  contactName: resolved.contactName,
22583
+ suinsName: resolved.suinsName,
22447
22584
  gasCost: gasResult.gasCostSui,
22448
22585
  gasCostUnit: "SUI",
22449
22586
  balance
22450
22587
  };
22451
22588
  }
22589
+ /**
22590
+ * [S.279 / CLI-CONTACTS-CLEANUP — 2026-05-23] Resolve a recipient
22591
+ * string into a canonical 0x address, trying each shape in priority
22592
+ * order:
22593
+ * 1. **hex** — `0x…` is used directly (no RPC round-trip).
22594
+ * 2. **SuiNS** — `alex.sui` resolves via `suix_resolveNameServiceAddress`.
22595
+ * 3. **saved contact** — `ContactManager.resolve()` falls back to the
22596
+ * legacy `~/.t2000/contacts.json` alias map. Deprecated; the
22597
+ * manager surfaces a one-time `console.warn` when this path fires.
22598
+ *
22599
+ * Returns `{ address, suinsName?, contactName? }` so `send()` can stamp
22600
+ * the name source on the receipt without re-resolving.
22601
+ *
22602
+ * Throws `T2000Error('SUINS_NOT_REGISTERED', …)` for well-formed but
22603
+ * unregistered SuiNS names (vs. propagating the engine-style
22604
+ * `SuinsNotRegisteredError` — keeps the SDK's error surface
22605
+ * `T2000Error`-only, consistent with every other write helper).
22606
+ *
22607
+ * [S.279.1 / 2026-05-23 — patch v2.19.1] Promoted from `private` to
22608
+ * `public` so MCP's `t2000_send` dryRun preview path can share the
22609
+ * same resolution logic as the live execute path. Pre-2.19.1 the
22610
+ * MCP dryRun called `agent.contacts.resolve(to)` directly, which
22611
+ * rejected SuiNS names — preview-then-execute flows broke for
22612
+ * `alex.sui`-style recipients. SSOT: never let preview and execute
22613
+ * resolve the same input differently.
22614
+ */
22615
+ async resolveRecipient(input) {
22616
+ const trimmed = input.trim();
22617
+ if (SUI_ADDRESS_REGEX.test(trimmed)) {
22618
+ return { address: trimmed.toLowerCase() };
22619
+ }
22620
+ if (looksLikeSuiNs(trimmed)) {
22621
+ try {
22622
+ const name = trimmed.toLowerCase();
22623
+ const address = await resolveSuinsViaRpc(name);
22624
+ if (!address) {
22625
+ throw new SuinsNotRegisteredError(name);
22626
+ }
22627
+ return { address: address.toLowerCase(), suinsName: name };
22628
+ } catch (err) {
22629
+ if (err instanceof SuinsNotRegisteredError) {
22630
+ throw new T2000Error(
22631
+ "SUINS_NOT_REGISTERED",
22632
+ err.message
22633
+ );
22634
+ }
22635
+ throw err;
22636
+ }
22637
+ }
22638
+ return this.contacts.resolve(input);
22639
+ }
22452
22640
  async balance() {
22453
22641
  const bal = await queryBalance(this.client, this._address);
22454
22642
  let chainTotal = bal.available + bal.gasReserve.usdEquiv;
@@ -24196,6 +24384,16 @@ export {
24196
24384
  parseSuiRpcTx,
24197
24385
  extractTxSender,
24198
24386
  extractTxCommands,
24387
+ SUI_ADDRESS_REGEX,
24388
+ SUI_ADDRESS_STRICT_REGEX,
24389
+ SUINS_NAME_REGEX,
24390
+ InvalidAddressError,
24391
+ SuinsNotRegisteredError,
24392
+ SuinsRpcError,
24393
+ looksLikeSuiNs,
24394
+ resolveSuinsViaRpc,
24395
+ resolveAddressToSuinsViaRpc,
24396
+ normalizeAddressInput,
24199
24397
  getRates,
24200
24398
  getPendingRewardsByAddress,
24201
24399
  getPendingRewards,
@@ -24257,4 +24455,4 @@ axios/dist/node/axios.cjs:
24257
24455
  @scure/bip39/index.js:
24258
24456
  (*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
24259
24457
  */
24260
- //# sourceMappingURL=chunk-ZNZ4MOY2.js.map
24458
+ //# sourceMappingURL=chunk-R7KXQRHQ.js.map