{"version":3,"sources":["../src/errors.ts","../src/token-registry.ts","../src/wallet/coinSelection.ts","../src/wallet/keypairSigner.ts","../src/wallet/zkLoginSigner.ts","../src/wallet/pay.ts","../src/wallet/executeTx.ts","../src/preflight.ts","../src/browser.ts","../src/constants.ts","../src/utils/sui.ts","../src/wallet/classify.ts","../src/wallet/history.ts","../src/utils/format.ts","../src/utils/base64.ts","../src/protocols/cetus-swap.ts","../src/wallet/send.ts"],"names":["T2000Error","COIN_REGISTRY","ETH_TYPE","IKA_TYPE","LOFI_TYPE","MANIFEST_TYPE","NAVX_TYPE","SUI_TYPE","TOKEN_MAP","USDC_TYPE","USDE_TYPE","USDSUI_TYPE","USDT_TYPE","WAL_TYPE","WBTC_TYPE","coinWithBalance","requested","swapAll","effectiveAmount","coin","Transaction","isValidSuiAddress","normalizeSuiAddress","fromBase64","buildCoinToAddressBalanceMigration","SuiGrpcClient","getDecimalsForCoinType","AggregatorClient","Env"],"mappings":"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","file":"browser.cjs","sourcesContent":["export type T2000ErrorCode =\n  | 'INSUFFICIENT_BALANCE'\n  | 'ADDRESS_BALANCE_UNSPONSORABLE'\n  | 'INSUFFICIENT_GAS'\n  | 'INVALID_ADDRESS'\n  | 'INVALID_AMOUNT'\n  | 'WALLET_NOT_FOUND'\n  | 'WALLET_LOCKED'\n  | 'WALLET_EXISTS'\n  | 'WALLET_CORRUPT'\n  | 'INVALID_KEY'\n  | 'SIMULATION_FAILED'\n  | 'TRANSACTION_FAILED'\n  | 'ASSET_NOT_SUPPORTED'\n  | 'INVALID_ASSET'\n  | 'PROTOCOL_UNAVAILABLE'\n  | 'RPC_ERROR'\n  | 'RPC_UNREACHABLE'\n  | 'PRICE_EXCEEDS_LIMIT'\n  | 'UNSUPPORTED_NETWORK'\n  | 'PAYMENT_EXPIRED'\n  | 'DUPLICATE_PAYMENT'\n  | 'FACILITATOR_REJECTION'\n  | 'CONTACT_NOT_FOUND'\n  | 'INVALID_CONTACT_NAME'\n  | 'SUINS_NOT_REGISTERED'\n  | 'FACILITATOR_TIMEOUT'\n  | 'SAFEGUARD_BLOCKED'\n  | 'SWAP_NO_ROUTE'\n  | 'SWAP_FAILED'\n  | 'CHAIN_MODE_INVALID'\n  | 'UNKNOWN';\n\nexport interface T2000ErrorData {\n  reason?: string;\n  [key: string]: unknown;\n}\n\nexport class T2000Error extends Error {\n  readonly code: T2000ErrorCode;\n  readonly data?: T2000ErrorData;\n  readonly retryable: boolean;\n\n  constructor(code: T2000ErrorCode, message: string, data?: T2000ErrorData, retryable = false) {\n    super(message);\n    this.name = 'T2000Error';\n    this.code = code;\n    this.data = data;\n    this.retryable = retryable;\n  }\n\n  toJSON() {\n    return {\n      error: this.code,\n      message: this.message,\n      ...(this.data && { data: this.data }),\n      retryable: this.retryable,\n    };\n  }\n}\n\nexport function mapWalletError(error: unknown): T2000Error {\n  const msg = error instanceof Error ? error.message : String(error);\n\n  if (msg.includes('rejected') || msg.includes('cancelled')) {\n    return new T2000Error('TRANSACTION_FAILED', 'Transaction cancelled');\n  }\n  if (msg.includes('Insufficient') || msg.includes('insufficient')) {\n    return new T2000Error('INSUFFICIENT_BALANCE', 'Insufficient balance');\n  }\n\n  return new T2000Error('UNKNOWN', msg, undefined, true);\n}\n\nexport function mapMoveAbortCode(code: number): string {\n  const abortMessages: Record<number, string> = {\n    1: 'Protocol is temporarily paused',\n    2: 'Amount must be greater than zero',\n    3: 'Invalid operation type',\n    4: 'Fee rate exceeds maximum',\n    5: 'Insufficient treasury balance',\n    6: 'Not authorized',\n    7: 'Package version mismatch — upgrade required',\n    8: 'Timelock is active — wait for expiry',\n    9: 'No pending change to execute',\n    10: 'Already at current version',\n    // NAVI Protocol abort codes\n    1502: 'Oracle price is stale — try again in a moment',\n    1503: 'Withdrawal amount is invalid (zero or dust) — try a specific amount instead of \"all\"',\n    1600: 'Health factor too low — withdrawal would risk liquidation',\n    1605: 'Asset borrowing is disabled or at capacity on this protocol',\n    // NAVI utils abort codes\n    46000: 'Insufficient balance to repay — withdraw some savings first to get cash',\n  };\n  return abortMessages[code] ?? `Move abort code: ${code}`;\n}\n\n/**\n * Check if an error message contains a MoveAbort — these are on-chain\n * failures that will fail no matter how many times you retry.\n */\nexport function isMoveAbort(msg: string): boolean {\n  return msg.includes('MoveAbort') || msg.includes('MovePrimitiveRuntimeError');\n}\n\nexport function parseMoveAbortMessage(msg: string): string {\n  const abortMatch = msg.match(/abort code:\\s*(\\d+)/i) ?? msg.match(/MoveAbort[^,]*,\\s*(\\d+)/);\n  if (abortMatch) {\n    const code = parseInt(abortMatch[1], 10);\n\n    const moduleMatch = msg.match(/Identifier\\(\"([^\"]+)\"\\)/) ?? msg.match(/in '([^']+)'/);\n    const fnMatch = msg.match(/function_name:\\s*Some\\(\"([^\"]+)\"\\)/);\n    const context = `${moduleMatch?.[1] ?? ''}${fnMatch ? `::${fnMatch[1]}` : ''}`.toLowerCase();\n    const suffix = moduleMatch\n      ? ` [${moduleMatch[1]}${fnMatch ? `::${fnMatch[1]}` : ''}]`\n      : '';\n\n    if (context.includes('slippage')) {\n      return `Slippage too high — price moved during execution${suffix}`;\n    }\n    if (context.includes('balance::split') || context.includes('balance::ENotEnough')) {\n      return `Insufficient on-chain balance${suffix}`;\n    }\n\n    const mapped = mapMoveAbortCode(code);\n    return `${mapped}${suffix}`;\n  }\n  return msg;\n}\n","/**\n * Unified token registry — single source of truth for coin types, decimals,\n * and symbols. ZERO heavy dependencies; safe to import anywhere (server,\n * browser, Edge).\n *\n * Used for swap-by-symbol resolution, amount formatting (decimals), and\n * history/balance display. USDC is the settlement stable (send / receive /\n * x402 pay); everything else is holdable / swappable. There is no curated\n * \"tier\" gate — swaps accept any coin type (Cetus routes); the registry just\n * makes the common tokens ergonomic to reference by symbol.\n *\n * To add a token: add ONE entry to COIN_REGISTRY below — everything derives\n * from it.\n */\n\nexport interface CoinMeta {\n  type: string;\n  decimals: number;\n  symbol: string;\n}\n\n/**\n * Canonical coin registry.\n * Key = user-friendly name (used in swap, CLI, prompts).\n */\nexport const COIN_REGISTRY: Record<string, CoinMeta> = {\n  // ── Settlement stable ─────────────────────────────────────────────────\n  USDC:     { type: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC', decimals: 6, symbol: 'USDC' },\n\n  // ── Common swap assets (symbol ergonomics — any coin type still swaps) ─\n  SUI:      { type: '0x2::sui::SUI', decimals: 9, symbol: 'SUI' },\n  wBTC:     { type: '0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC', decimals: 8, symbol: 'wBTC' },\n  ETH:      { type: '0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH', decimals: 8, symbol: 'ETH' },\n  GOLD:     { type: '0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM', decimals: 9, symbol: 'GOLD' },\n  DEEP:     { type: '0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP', decimals: 6, symbol: 'DEEP' },\n  WAL:      { type: '0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL', decimals: 9, symbol: 'WAL' },\n  NS:       { type: '0x5145494a5f5100e645e4b0aa950fa6b68f614e8c59e17bc5ded3495123a79178::ns::NS', decimals: 6, symbol: 'NS' },\n  IKA:      { type: '0x7262fb2f7a3a14c888c438a3cd9b912469a58cf60f367352c46584262e8299aa::ika::IKA', decimals: 9, symbol: 'IKA' },\n  CETUS:    { type: '0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS', decimals: 9, symbol: 'CETUS' },\n  NAVX:     { type: '0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX', decimals: 9, symbol: 'NAVX' },\n  vSUI:     { type: '0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT', decimals: 9, symbol: 'vSUI' },\n  haSUI:    { type: '0xbde4ba4c2e274a60ce15c1cfff9e5c42e41654ac8b6d906a57efa4bd3c29f47d::hasui::HASUI', decimals: 9, symbol: 'haSUI' },\n  afSUI:    { type: '0xf325ce1300e8dac124071d3152c5c5ee6174914f8bc2161e88329cf579246efc::afsui::AFSUI', decimals: 9, symbol: 'afSUI' },\n  LOFI:     { type: '0xf22da9a24ad027cccb5f2d496cbe91de953d363513db08a3a734d361c7c17503::LOFI::LOFI', decimals: 9, symbol: 'LOFI' },\n  MANIFEST: { type: '0xc466c28d87b3d5cd34f3d5c088751532d71a38d93a8aae4551dd56272cfb4355::manifest::MANIFEST', decimals: 9, symbol: 'MANIFEST' },\n\n  // ── Other stables (display / classification) ──────────────────────────\n  USDT:     { type: '0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT', decimals: 6, symbol: 'USDT' },\n  USDe:     { type: '0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE', decimals: 6, symbol: 'USDe' },\n  USDSUI:   { type: '0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI', decimals: 6, symbol: 'USDsui' },\n};\n\n/** Reverse lookup: coin type → CoinMeta. Built once at import time. */\nconst BY_TYPE = new Map<string, CoinMeta>();\nfor (const meta of Object.values(COIN_REGISTRY)) {\n  BY_TYPE.set(meta.type, meta);\n}\n\n// ── Lookup helpers ───────────────────────────────────────────────────────\n\n/**\n * Returns the registry metadata for a coin type, or `undefined` if the coin\n * is not in the registry. Used for canonical-symbol + decimal resolution.\n */\nexport function getCoinMeta(coinType: string): CoinMeta | undefined {\n  return BY_TYPE.get(coinType);\n}\n\n/**\n * Returns true if the coin type appears in COIN_REGISTRY. Useful as a\n * canonical-symbol gate — e.g. so the registry's mixed-case `USDsui` wins\n * over a vendor feed's uppercase `USDSUI`.\n */\nexport function isInRegistry(coinType: string): boolean {\n  return BY_TYPE.has(coinType);\n}\n\n// ── Lookup helpers ───────────────────────────────────────────────────────\n\n/**\n * Get decimals for any coin type. Checks full type match, then suffix match,\n * then defaults to 9. Works for any token, registered or not.\n */\nexport function getDecimalsForCoinType(coinType: string): number {\n  const direct = BY_TYPE.get(coinType);\n  if (direct) return direct.decimals;\n\n  const suffix = coinType.split('::').slice(1).join('::').toUpperCase();\n  if (suffix) {\n    for (const meta of BY_TYPE.values()) {\n      const metaSuffix = meta.type.split('::').slice(1).join('::').toUpperCase();\n      if (metaSuffix === suffix) return meta.decimals;\n    }\n  }\n\n  return 9;\n}\n\n/** Minimal structural client — just the coin-metadata read this needs. */\ninterface CoinMetadataClient {\n  core: { getCoinMetadata: (opts: { coinType: string }) => Promise<unknown> };\n}\n\n/**\n * [2.11] Decimals for any coin type, resolved correctly for arbitrary tokens.\n * Registry tokens return their known decimals (no network). A coin type NOT in\n * the registry is read ON-CHAIN via coin metadata — never the 9-default guess,\n * because a wrong `from` decimal would corrupt a swap's input amount\n * (financial-amounts rule). Falls back to the registry heuristic only if the\n * on-chain read fails or returns no usable decimals.\n */\nexport async function resolveCoinDecimals(\n  client: CoinMetadataClient,\n  coinType: string,\n): Promise<number> {\n  if (isInRegistry(coinType)) return getDecimalsForCoinType(coinType);\n  try {\n    const res = (await client.core.getCoinMetadata({ coinType })) as {\n      metadata?: { decimals?: number };\n      decimals?: number;\n    };\n    const d = res?.metadata?.decimals ?? res?.decimals;\n    if (typeof d === 'number' && Number.isFinite(d)) return d;\n  } catch {\n    /* on-chain read failed — fall through to the registry heuristic */\n  }\n  return getDecimalsForCoinType(coinType);\n}\n\n/**\n * Resolve a full coin type to a user-friendly symbol.\n * Returns the last `::` segment if not in the registry.\n */\nexport function resolveSymbol(coinType: string): string {\n  const direct = BY_TYPE.get(coinType);\n  if (direct) return direct.symbol;\n\n  const suffix = coinType.split('::').slice(1).join('::').toUpperCase();\n  if (suffix) {\n    for (const meta of BY_TYPE.values()) {\n      const metaSuffix = meta.type.split('::').slice(1).join('::').toUpperCase();\n      if (metaSuffix === suffix) return meta.symbol;\n    }\n  }\n\n  return coinType.split('::').pop() ?? coinType;\n}\n\n/**\n * Name → type map for swap resolution. Derived from COIN_REGISTRY.\n * Contains BOTH original-case and UPPERCASE keys for case-insensitive lookup.\n */\nexport const TOKEN_MAP: Record<string, string> = (() => {\n  const map: Record<string, string> = {};\n  for (const [name, meta] of Object.entries(COIN_REGISTRY)) {\n    map[name] = meta.type;\n    map[name.toUpperCase()] = meta.type;\n  }\n  return map;\n})();\n\n/**\n * Resolve a user-friendly token name to its full coin type.\n * Returns the input unchanged if already a full coin type (contains \"::\").\n * Case-insensitive: 'usde', 'USDe', 'USDE' all resolve correctly.\n */\nexport function resolveTokenType(nameOrType: string): string | null {\n  if (nameOrType.includes('::')) return nameOrType;\n  return TOKEN_MAP[nameOrType] ?? TOKEN_MAP[nameOrType.toUpperCase()] ?? null;\n}\n\n/** Common type constants for direct import. */\nexport const SUI_TYPE = COIN_REGISTRY.SUI.type;\nexport const USDC_TYPE = COIN_REGISTRY.USDC.type;\nexport const USDT_TYPE = COIN_REGISTRY.USDT.type;\nexport const USDSUI_TYPE = COIN_REGISTRY.USDSUI.type;\nexport const USDE_TYPE = COIN_REGISTRY.USDe.type;\nexport const ETH_TYPE = COIN_REGISTRY.ETH.type;\nexport const WBTC_TYPE = COIN_REGISTRY.wBTC.type;\nexport const WAL_TYPE = COIN_REGISTRY.WAL.type;\nexport const NAVX_TYPE = COIN_REGISTRY.NAVX.type;\nexport const IKA_TYPE = COIN_REGISTRY.IKA.type;\nexport const LOFI_TYPE = COIN_REGISTRY.LOFI.type;\nexport const MANIFEST_TYPE = COIN_REGISTRY.MANIFEST.type;\n","/**\n * Wallet-side coin selection helpers — single source of truth for the\n * \"produce a `Coin<T>` argument holding `amount` raw units of `coinType`,\n * owned by `address`\" pattern. Used by every wallet-mode appender that\n * needs a coin input (save, send, swap, repay, stake, etc.).\n *\n * **2026-05-22 — address-balance migration.** Sui mainnet's address-balance\n * feature ships funds account-style instead of as discrete `Coin<T>` objects.\n * After a payment via `0x2::balance::send_funds`, the leftover lands in the\n * sender's address balance with a synthetic \"coin reservation\" representing\n * the deposit. `client.getCoins()` correctly filters those reservations out\n * (they aren't real owned objects), so the old fetch+merge+split pattern\n * threw `INSUFFICIENT_BALANCE` for users whose stables had drifted into\n * address balance — even when `getBalance().totalBalance` showed plenty.\n *\n * The fix is structural: hand the work to `coinWithBalance({ type, balance })`\n * from `@mysten/sui/transactions`. Its build-time resolver inspects coins +\n * address balance together (`getBalance` + `listCoins`), then emits the\n * right shape — direct `redeem_funds` from address balance when AB ≥\n * required, or merge-and-split across coins + AB withdrawal when not. Multi\n * intents per coin type get batched into a single merge in one PTB, so the\n * old per-PTB merge cache is no longer needed.\n *\n * Pre-flight uses `client.getBalance().totalBalance` (sums coins + AB)\n * instead of summing the paginated `getCoins` page. That's the OTHER half\n * of the migration — the legacy path could see `0` from `getCoins` and\n * mistakenly throw before `coinWithBalance` ever ran.\n */\nimport {\n  Transaction,\n  coinWithBalance,\n  type TransactionObjectArgument,\n} from '@mysten/sui/transactions';\nimport type { SuiCoreClient } from '../utils/sui.js';\nimport { T2000Error } from '../errors.js';\n\nexport interface CoinPage {\n  ids: string[];\n  totalBalance: bigint;\n}\n\n/**\n * Sum every coin of `coinType` owned by `owner`, INCLUDING address balance.\n * Returns the IDs of any discrete coin objects that exist (callers\n * occasionally need this for non-`coinWithBalance` paths, e.g. SUIns name\n * registration which expects raw object IDs).\n *\n * Pre-2026-05-22 this function paginated `client.getCoins` and summed\n * the page balances. That misses address-balance funds (the SDK filters\n * them out of `getCoins` for back-compat). The new implementation calls\n * `getBalance` for the canonical total and `getCoins` for the optional\n * ID list — both round-trips, but they happen in parallel.\n */\nexport async function fetchAllCoins(\n  client: SuiCoreClient,\n  owner: string,\n  coinType: string,\n): Promise<CoinPage> {\n  const [balance, ids] = await Promise.all([\n    client.core.getBalance({ owner, coinType }),\n    (async () => {\n      const out: string[] = [];\n      let cursor: string | null | undefined;\n      let hasNext = true;\n      while (hasNext) {\n        const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? undefined });\n        for (const c of page.objects) out.push(c.objectId);\n        cursor = page.cursor;\n        hasNext = page.hasNextPage;\n      }\n      return out;\n    })(),\n  ]);\n  return { ids, totalBalance: BigInt(balance.balance.balance) };\n}\n\nexport interface SelectAndSplitResult {\n  /** TransactionObjectArgument for a coin holding `effectiveAmount` raw units. */\n  coin: TransactionObjectArgument;\n  /** Actual raw amount the returned coin holds. May be < requested if `swapAll` is true. */\n  effectiveAmount: bigint;\n  /** True iff the request consumed the entire wallet balance (no split needed). */\n  swapAll: boolean;\n}\n\n/**\n * Wallet-mode coin selection prelude. Pre-flights against\n * `getBalance().totalBalance` (coins + address balance combined), then\n * returns a `coinWithBalance({ type, balance })` argument that the\n * `@mysten/sui` resolver fulfills at build time.\n *\n * Throws `T2000Error` (`INSUFFICIENT_BALANCE`) when:\n * - `amount` is bigger than the total balance AND the caller did NOT\n *   opt into `swapAll: true` clipping.\n * - `amount === 'all'` AND total balance is zero.\n *\n * @param tx — PTB to register the `coinWithBalance` intent against.\n * @param client — Sui RPC client for the pre-flight `getBalance` lookup.\n * @param owner — wallet address whose coins to source from.\n * @param coinType — fully-qualified Sui coin type (e.g. `\"0x...::usdc::USDC\"`).\n * @param amount — raw amount to source (in MIST / smallest unit). Pass\n *   `'all'` to consume the entire balance.\n * @param options.allowSwapAll — if true (default), `amount` >= totalBalance\n *   auto-clips to total. If false, throws when the request would over-consume.\n * @param options.sponsoredContext — when true, source ONLY from discrete coin\n *   objects (never the address balance). See the long note below — this exists\n *   because Enoki's gas station can't yet deserialize a `TransactionData` that\n *   contains the address-balance `FundsWithdrawal` reservation that\n *   `coinWithBalance` emits. Self-funded callers leave this false: the fullnode\n *   handles `FundsWithdrawal` fine, so the address-balance path is preferred\n *   (it can reach funds that aren't held as coin objects).\n *\n * @returns\n *   - `coin` — `TransactionObjectArgument` ready for downstream consumption.\n *   - `effectiveAmount` — the raw amount the returned coin holds (handles\n *     swapAll clipping).\n *   - `swapAll` — true iff the entire balance was consumed.\n */\nexport async function selectAndSplitCoin(\n  tx: Transaction,\n  client: SuiCoreClient,\n  owner: string,\n  coinType: string,\n  amount: bigint | 'all',\n  options: {\n    allowSwapAll?: boolean;\n    sponsoredContext?: boolean;\n    mergeCache?: SponsoredCoinMergeCache;\n  } = {},\n): Promise<SelectAndSplitResult> {\n  // [2026-05-30] Sponsored Enoki path — coin objects only. `coinWithBalance`\n  // reaches into the address balance (mysten Address Balances feature) when\n  // `addressBalance >= required`, emitting `0x2::coin::redeem_funds` + a\n  // `FundsWithdrawal` reservation input. That input is a newer `TransactionData`\n  // field; Enoki's sponsor endpoint accepts the kind bytes (200) but its gas\n  // station rejects the assembled `TransactionData` at execute with\n  // \"Invalid bcs bytes for TransactionData\". The fullnode parses it fine — only\n  // Enoki can't (yet). So under sponsorship we source from discrete coin objects\n  // and surface a clear error when the user's funds are address-balance-only.\n  // See github.com/mission69b/t2000 issue #93.\n  if (options.sponsoredContext) {\n    return selectCoinObjectsOnly(\n      tx,\n      client,\n      owner,\n      coinType,\n      amount,\n      options.allowSwapAll ?? true,\n      options.mergeCache,\n    );\n  }\n\n  const balanceResp = await client.core.getBalance({ owner, coinType });\n  const totalBalance = BigInt(balanceResp.balance.balance);\n\n  if (totalBalance === 0n) {\n    throw new T2000Error('INSUFFICIENT_BALANCE', `No balance found for ${coinType}`);\n  }\n\n  const allowSwapAll = options.allowSwapAll ?? true;\n\n  if (amount !== 'all' && amount > totalBalance && !allowSwapAll) {\n    throw new T2000Error('INSUFFICIENT_BALANCE', `Insufficient balance for ${coinType}`, {\n      available: totalBalance.toString(),\n      required: amount.toString(),\n    });\n  }\n\n  const requested = amount === 'all' ? totalBalance : amount;\n  const swapAll = amount === 'all' || requested >= totalBalance;\n  const effectiveAmount = swapAll ? totalBalance : requested;\n\n  const coin = coinWithBalance({ type: coinType, balance: effectiveAmount })(tx);\n\n  return { coin, effectiveAmount, swapAll };\n}\n\n/**\n * Coin-object-only selection for sponsored (Enoki) transactions. Fetches the\n * owner's discrete `Coin<T>` objects (NOT the address balance — `getCoins`\n * excludes it), merges them, and splits the requested amount. Never emits a\n * `FundsWithdrawal` reservation, so the resulting `TransactionData` stays on\n * the shape Enoki's gas station can serialize.\n *\n * Throws `ADDRESS_BALANCE_UNSPONSORABLE` when the coin objects don't cover the\n * request — which, for a user whose `getBalance().totalBalance` shows funds,\n * means those funds live in the address balance (e.g. received via a gasless\n * stablecoin transfer) and can't be moved through a sponsored transaction yet.\n */\n/**\n * Per-PTB cache of merged sponsored coin-object primaries, keyed by coin\n * type. The FIRST `selectCoinObjectsOnly` call for a given coin type in a\n * PTB fetches the owner's discrete `Coin<T>` objects, merges them into one\n * `primary`, and records it here alongside the remaining (unspent) balance.\n * EVERY subsequent leg sourcing the same coin type splits from that cached\n * `primary` instead of re-fetching + re-merging.\n *\n * Why this exists (S.xxx, 2026-06-02): a sponsored bundle with 2+ legs\n * sourcing the same coin (e.g. `SUI→WAL` + `SUI→DEEP`, or `swap USDC` +\n * `save USDC`) called `selectCoinObjectsOnly` once per leg. Each call\n * emitted its own `mergeCoins` over the SAME coin objects, so the second\n * leg's merge referenced coins the first leg already consumed → Enoki\n * dry-run failed with `CommandArgumentError { ArgumentWithoutValue }`.\n *\n * This is NOT SUI-specific. Under sponsorship, `selectAndSplitCoin` routes\n * EVERY asset through `selectCoinObjectsOnly` (the `coinWithBalance`\n * batching that would otherwise dedup these merges only runs for\n * non-sponsored CLI/direct flows — its address-balance `FundsWithdrawal`\n * reservation is what Enoki can't deserialize, issue #93). So the cache is\n * the dedup layer for ALL coin types in a sponsored multi-leg PTB, keyed\n * by coin type. SUI was simply the first asset observed failing in the\n * wild because it's the most common swap source.\n */\nexport type SponsoredCoinMergeCache = Map<\n  string,\n  { primary: TransactionObjectArgument; remaining: bigint }\n>;\n\nasync function selectCoinObjectsOnly(\n  tx: Transaction,\n  client: SuiCoreClient,\n  owner: string,\n  coinType: string,\n  amount: bigint | 'all',\n  allowSwapAll: boolean,\n  mergeCache?: SponsoredCoinMergeCache,\n): Promise<SelectAndSplitResult> {\n  // Cache hit — a prior leg in THIS PTB already merged this coin type's\n  // objects into `cached.primary`. Re-running the fetch+merge below would\n  // emit a second `mergeCoins` over already-consumed coins → dry-run\n  // `ArgumentWithoutValue`. Split from the cached primary instead.\n  const cached = mergeCache?.get(coinType);\n  if (cached) {\n    const requested = amount === 'all' ? cached.remaining : amount;\n    if (cached.remaining === 0n || requested > cached.remaining) {\n      throw new T2000Error(\n        'ADDRESS_BALANCE_UNSPONSORABLE',\n        `Not enough ${coinType} in coin objects to cover all legs of this ` +\n          `sponsored bundle. The remaining funds are in your address balance, ` +\n          `which sponsored transactions can't access yet.`,\n        { remaining: cached.remaining.toString(), requested: requested.toString(), coinType },\n      );\n    }\n    const swapAll = amount === 'all' || requested >= cached.remaining;\n    const effectiveAmount = swapAll ? cached.remaining : requested;\n    const coin = swapAll\n      ? cached.primary\n      : tx.splitCoins(cached.primary, [effectiveAmount])[0];\n    cached.remaining -= effectiveAmount;\n    return { coin, effectiveAmount, swapAll };\n  }\n\n  const objects: { objectId: string; balance: bigint }[] = [];\n  let coinObjectTotal = 0n;\n  let cursor: string | null | undefined;\n  let hasNext = true;\n  while (hasNext) {\n    const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? undefined });\n    for (const c of page.objects) {\n      objects.push({ objectId: c.objectId, balance: BigInt(c.balance) });\n      coinObjectTotal += BigInt(c.balance);\n    }\n    cursor = page.cursor;\n    hasNext = page.hasNextPage;\n  }\n\n  const unsponsorable = (): T2000Error =>\n    new T2000Error(\n      'ADDRESS_BALANCE_UNSPONSORABLE',\n      `These funds are in your address balance, which sponsored transactions ` +\n        `can't access yet. (Funds received via gasless transfers land there.) ` +\n        `This will work once the gas sponsor adds address-balance support.`,\n      { coinObjectTotal: coinObjectTotal.toString(), coinType },\n    );\n\n  if (coinObjectTotal === 0n) {\n    throw unsponsorable();\n  }\n\n  const requested = amount === 'all' ? coinObjectTotal : amount;\n  if (requested > coinObjectTotal) {\n    // Not enough in coin objects. If the caller allows clipping to the\n    // available coin-object total (\"swap all\"), do so; otherwise the shortfall\n    // is sitting in the address balance → unsponsorable.\n    if (allowSwapAll && amount === 'all') {\n      // unreachable (requested === coinObjectTotal here) — kept for clarity.\n    } else {\n      throw unsponsorable();\n    }\n  }\n\n  const swapAll = amount === 'all' || requested >= coinObjectTotal;\n  const effectiveAmount = swapAll ? coinObjectTotal : requested;\n\n  const [first, ...rest] = objects;\n  const primary = tx.object(first.objectId);\n  if (rest.length > 0) {\n    tx.mergeCoins(\n      primary,\n      rest.map((o) => tx.object(o.objectId)),\n    );\n  }\n\n  // Consume the whole merged coin when taking everything; otherwise split the\n  // exact amount and leave the remainder on the (sender-owned) primary coin.\n  const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];\n\n  // Record the merged primary so later legs in the same PTB reuse it\n  // rather than re-fetching + re-merging the same (now-consumed) coins.\n  // When `swapAll`, the primary was consumed (remaining 0) — a later leg\n  // hitting the cache then throws the unsponsorable shortfall above.\n  mergeCache?.set(coinType, {\n    primary,\n    remaining: coinObjectTotal - effectiveAmount,\n  });\n\n  return { coin, effectiveAmount, swapAll };\n}\n\n/**\n * Build a GASLESS coin→address-balance migration.\n *\n * Sends WHOLE `Coin<T>` objects to `owner`'s OWN SIP-58 address balance via\n * `0x2::coin::send_funds(coin, owner)` — one allowlisted framework MoveCall\n * per coin, with NO native `SplitCoins` / `MergeCoins` commands. That shape is\n * what makes it gasless: the gRPC build resolver only zeros gas for PTBs whose\n * every command is a MoveCall into the `0x2` gasless allowlist (`send_funds` /\n * `redeem_funds` / `withdrawal_split` / `into_balance`). The same eligibility\n * the gasless `balance::send_funds` send + the x402 `redeem_funds`+`send_funds`\n * withdrawal rely on. (The old migration merged+split first → native commands →\n * fell outside the allowlist → needed SUI. See `pay.ts ensureAddressBalanceCovers`.)\n *\n * Selects the FEWEST coins (largest first) whose combined balance covers\n * `minAmount`, minimizing the MoveCall count. Over-migration is harmless — the\n * surplus just lands in the address balance. Pure / network-free: callers pass\n * coins they've already fetched, so this is unit-testable offline.\n *\n * Throws `INSUFFICIENT_BALANCE` when the supplied coin objects can't cover\n * `minAmount`.\n */\nexport function buildCoinToAddressBalanceMigration(args: {\n  coins: { objectId: string; balance: bigint }[];\n  coinType: string;\n  owner: string;\n  minAmount: bigint;\n}): { tx: Transaction; migratedRaw: bigint } {\n  const { coins, coinType, owner, minAmount } = args;\n\n  // Largest first → fewest whole-coin sends to cover the shortfall.\n  const sorted = [...coins]\n    .filter((c) => c.balance > 0n)\n    .sort((a, b) => (b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0));\n\n  const selected: { objectId: string; balance: bigint }[] = [];\n  let migratedRaw = 0n;\n  for (const c of sorted) {\n    if (migratedRaw >= minAmount) break;\n    selected.push(c);\n    migratedRaw += c.balance;\n  }\n\n  if (migratedRaw < minAmount) {\n    throw new T2000Error('INSUFFICIENT_BALANCE', `Insufficient ${coinType} coin objects to migrate`, {\n      available: migratedRaw.toString(),\n      required: minAmount.toString(),\n    });\n  }\n\n  const tx = new Transaction();\n  for (const c of selected) {\n    tx.moveCall({\n      target: '0x2::coin::send_funds',\n      typeArguments: [coinType],\n      arguments: [tx.object(c.objectId), tx.pure.address(owner)],\n    });\n  }\n  return { tx, migratedRaw };\n}\n\n/**\n * SUI-specific coin selection. Branches on sponsorship context:\n *\n * - **Self-funded (`sponsoredContext: false`)** — splits from `tx.gas`\n *   directly (the user's gas coin IS their SUI). More efficient — no\n *   `getBalance` RTT.\n *\n * - **Sponsored (`sponsoredContext: true`)** — sources from the user's\n *   discrete SUI coin objects (`selectCoinObjectsOnly`). This both (a) avoids\n *   `tx.gas`, which belongs to the Enoki sponsor — NOT the user — under\n *   sponsored flows (the original S.260 reason for `useGasCoin: false`), AND\n *   (b) avoids `coinWithBalance`'s address-balance `FundsWithdrawal`, which\n *   Enoki's gas station can't deserialize (issue #93). If the user's SUI is\n *   address-balance-only, it raises `ADDRESS_BALANCE_UNSPONSORABLE`.\n */\nexport async function selectSuiCoin(\n  tx: Transaction,\n  client: SuiCoreClient,\n  owner: string,\n  amountMist: bigint,\n  sponsoredContext: boolean,\n  mergeCache?: SponsoredCoinMergeCache,\n): Promise<SelectAndSplitResult> {\n  if (sponsoredContext) {\n    const { SUI_TYPE } = await import('../token-registry.js');\n    return selectCoinObjectsOnly(tx, client, owner, SUI_TYPE, amountMist, false, mergeCache);\n  }\n\n  const [coin] = tx.splitCoins(tx.gas, [amountMist]);\n  return { coin, effectiveAmount: amountMist, swapAll: false };\n}\n","import type { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\nimport type { TransactionSigner } from '../signer.js';\n\nexport class KeypairSigner implements TransactionSigner {\n  constructor(private readonly keypair: Ed25519Keypair) {}\n\n  getAddress(): string {\n    return this.keypair.getPublicKey().toSuiAddress();\n  }\n\n  async signTransaction(txBytes: Uint8Array): Promise<{ signature: string }> {\n    return this.keypair.signTransaction(txBytes);\n  }\n\n  async signPersonalMessage(messageBytes: Uint8Array): Promise<{ signature: string; bytes: string }> {\n    return this.keypair.signPersonalMessage(messageBytes);\n  }\n\n  /** Access the underlying keypair for APIs that still require it directly. */\n  getKeypair(): Ed25519Keypair {\n    return this.keypair;\n  }\n}\n","import type { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';\nimport type { TransactionSigner } from '../signer.js';\n\nexport interface ZkLoginProof {\n  proofPoints: {\n    a: string[];\n    b: string[][];\n    c: string[];\n  };\n  issBase64Details: {\n    indexMod4: number;\n    value: string;\n  };\n  headerBase64: string;\n  addressSeed: string;\n}\n\nexport class ZkLoginSigner implements TransactionSigner {\n  constructor(\n    private readonly ephemeralKeypair: Ed25519Keypair,\n    private readonly zkProof: ZkLoginProof,\n    private readonly userAddress: string,\n    private readonly maxEpoch: number,\n  ) {}\n\n  getAddress(): string {\n    return this.userAddress;\n  }\n\n  async signTransaction(txBytes: Uint8Array): Promise<{ signature: string }> {\n    const { getZkLoginSignature } = await import('@mysten/zklogin');\n    const ephSig = await this.ephemeralKeypair.signTransaction(txBytes);\n    return {\n      signature: getZkLoginSignature({\n        inputs: this.zkProof,\n        maxEpoch: this.maxEpoch,\n        userSignature: ephSig.signature,\n      }),\n    };\n  }\n\n  async signPersonalMessage(messageBytes: Uint8Array): Promise<{ signature: string; bytes: string }> {\n    const { getZkLoginSignature } = await import('@mysten/zklogin');\n    const { signature: ephSig, bytes } = await this.ephemeralKeypair.signPersonalMessage(messageBytes);\n    return {\n      signature: getZkLoginSignature({\n        inputs: this.zkProof,\n        maxEpoch: this.maxEpoch,\n        userSignature: ephSig,\n      }),\n      bytes,\n    };\n  }\n\n  isExpired(currentEpoch: number): boolean {\n    return currentEpoch >= this.maxEpoch;\n  }\n}\n","import type { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { fromBase64 } from '@mysten/sui/utils';\nimport type { X402Requirements } from '@suimpp/mpp/x402';\nimport type { TransactionSigner } from '../signer.js';\nimport type { PayOptions, PayResult } from '../types.js';\nimport { T2000Error } from '../errors.js';\nimport { executeTx } from './executeTx.js';\nimport {\n  type PreflightResult,\n  PREFLIGHT_OK,\n  preflightFail,\n  checkPositiveAmount,\n} from '../preflight.js';\n\n/**\n * Synchronous, network-free preflight for `pay` (x402 Service call). Validates\n * the target URL shape and the `maxPrice` ceiling when present — the cheap\n * checks the v3 host runs before dispatching the paid tool / showing the\n * tap-to-confirm card. Returns a `PreflightResult`; never throws. The probe +\n * 402 handshake + balance migration stay in `payWithMpp` (network).\n */\nexport function preflightPay(input: { url: string; maxPrice?: number }): PreflightResult {\n  if (typeof input.url !== 'string' || input.url.trim() === '') {\n    return preflightFail('FACILITATOR_REJECTION', 'A target URL is required to pay');\n  }\n  let parsed: URL;\n  try {\n    parsed = new URL(input.url);\n  } catch {\n    return preflightFail('FACILITATOR_REJECTION', `Invalid URL: ${input.url}`);\n  }\n  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n    return preflightFail(\n      'FACILITATOR_REJECTION',\n      `URL must be http(s): got ${parsed.protocol}//`,\n    );\n  }\n  // `maxPrice` is optional (no ceiling = pay whatever the 402 asks). Validate\n  // only when the caller set one — a malformed ceiling is a fat-finger.\n  if (input.maxPrice !== undefined) {\n    const priceCheck = checkPositiveAmount(input.maxPrice, 'maxPrice');\n    if (!priceCheck.valid) return priceCheck;\n  }\n  return PREFLIGHT_OK;\n}\n\n// ---------------------------------------------------------------------------\n// payWithMpp — the SDK's single source of truth for the pay loop. Browser-safe\n// (no fs / keyManager / SafeguardEnforcer), so the Audric client can run it\n// in-browser on the zkLogin session key. `T2000.pay()` delegates here.\n//\n// x402 `sui-exact`, ONE path (SPEC_AGENT_PAYMENTS_X402 1.2; scheme =\n// SUIMPP_X402_SCHEME.md v0.3). The flow: sign an authorization, the gateway\n// settles. The legacy MPP \"digest dialect\" (client broadcasts, retries with\n// the tx digest) was retired from the SDK — the gateway dual-serves both for\n// installed pre-x402 CLIs, so the SDK client doesn't need a fallback. Both\n// dialects always rode the SAME gasless `send_funds<USDC>` rail; the only\n// difference was who submits, and x402 is now the only one we speak.\n//\n// Settlement: the withdrawal form draws from the SIP-58 address balance (the\n// canonical stateless, offline-signable shape), so coin-object funds are\n// migrated in first when needed (S.414 finding). The client only SIGNS — the\n// gateway submits (settle-then-serve), so a failed upstream is never charged.\n// ---------------------------------------------------------------------------\n\nexport async function payWithMpp(args: {\n  signer: TransactionSigner;\n  client: SuiGrpcClient;\n  options: PayOptions;\n}): Promise<PayResult> {\n  const { signer, client, options } = args;\n\n  // Layer 2 — cheap synchronous preflight (URL shape + maxPrice sanity) before\n  // any network round-trip. Rethrow the precise code+message verbatim.\n  const pf = preflightPay({ url: options.url, maxPrice: options.maxPrice });\n  if (!pf.valid) throw new T2000Error(pf.code, pf.error);\n\n  const method = (options.method ?? 'GET').toUpperCase();\n  const canHaveBody = method !== 'GET' && method !== 'HEAD';\n  const reqInit: RequestInit = {\n    method,\n    headers: options.headers,\n    body: canHaveBody ? options.body : undefined,\n  };\n\n  // Probe (no payment). A paid endpoint answers 402; a free/cached one serves.\n  const probe = await fetch(options.url, reqInit);\n  if (probe.status !== 402) {\n    return finalize(probe, { paid: false });\n  }\n\n  const requirements = await pickSuiExactRequirements(probe, client.network);\n  if (!requirements) {\n    throw new T2000Error(\n      'FACILITATOR_REJECTION',\n      `Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. ` +\n        `This SDK only speaks the x402 dialect.`,\n    );\n  }\n\n  return payViaX402({ signer, client, options, reqInit, requirements });\n}\n\n// ---------------------------------------------------------------------------\n// x402 `sui-exact` — sign-then-settle\n// ---------------------------------------------------------------------------\n\nasync function pickSuiExactRequirements(\n  response: Response,\n  network: string,\n): Promise<X402Requirements | undefined> {\n  try {\n    const body = (await response.clone().json()) as { accepts?: X402Requirements[] };\n    const want = `sui:${network === 'testnet' ? 'testnet' : 'mainnet'}`;\n    return body.accepts?.find((a) => a.scheme === 'exact' && a.network === want);\n  } catch {\n    return undefined;\n  }\n}\n\nasync function payViaX402(args: {\n  signer: TransactionSigner;\n  client: SuiGrpcClient;\n  options: PayOptions;\n  reqInit: RequestInit;\n  requirements: X402Requirements;\n}): Promise<PayResult> {\n  const { signer, client, options, reqInit, requirements } = args;\n  const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import(\n    '@suimpp/mpp/x402'\n  );\n\n  const amountRaw = BigInt(requirements.maxAmountRequired);\n\n  // The x402 withdrawal form spends ONLY the SIP-58 address balance. A wallet\n  // funded by ordinary coin transfers (or swap output) holds Coin<USDC>\n  // objects → migrate enough in first (S.414 finding; SUIMPP_X402_SCHEME §4).\n  const migrationGasSui = await ensureAddressBalanceCovers({\n    signer,\n    client,\n    asset: requirements.asset,\n    amountRaw,\n  });\n\n  // Build + sign — NEVER submitted client-side; the gateway settles. The\n  // builder only reads toSuiAddress() + signTransaction(), both of which every\n  // TransactionSigner (keypair AND zkLogin) provides.\n  const signerAdapter = {\n    toSuiAddress: () => signer.getAddress(),\n    signTransaction: (bytes: Uint8Array) => signer.signTransaction(bytes),\n  } as unknown as Parameters<typeof buildX402SignedPayment>[0]['signer'];\n\n  const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });\n\n  const res = await fetch(options.url, {\n    ...reqInit,\n    headers: { ...(options.headers ?? {}), [X402_PAYMENT_HEADER]: header },\n  });\n\n  // Settled iff the gateway returned the x402 receipt header.\n  const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);\n  const paid = !!settleHeader;\n  let digest: string | undefined;\n  if (settleHeader) {\n    try {\n      digest = (JSON.parse(new TextDecoder().decode(fromBase64(settleHeader))) as { transaction?: string })\n        .transaction;\n    } catch {\n      digest = undefined;\n    }\n  }\n\n  const result = await finalize(res, { paid });\n  if (!paid) return { ...result, dialect: 'x402' };\n  return {\n    ...result,\n    dialect: 'x402',\n    cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),\n    gasCostSui: migrationGasSui,\n    receipt: digest\n      ? { reference: digest, timestamp: new Date().toISOString() }\n      : result.receipt,\n  };\n}\n\n/**\n * Ensure the sender's SIP-58 address balance covers `amountRaw` of `asset`.\n * Returns the SUI gas spent migrating (0 when no migration was needed or the\n * migration was gasless). Throws `INSUFFICIENT_BALANCE` when the wallet\n * doesn't hold enough of the asset at all (coins + address balance combined).\n */\nasync function ensureAddressBalanceCovers(args: {\n  signer: TransactionSigner;\n  client: SuiGrpcClient;\n  asset: string;\n  amountRaw: bigint;\n}): Promise<number> {\n  const { signer, client, asset, amountRaw } = args;\n  const owner = signer.getAddress();\n\n  // total = coins + address balance (the canonical combined read)\n  const balanceResp = await client.core.getBalance({ owner, coinType: asset });\n  const total = BigInt(balanceResp.balance.balance);\n  if (total < amountRaw) {\n    throw new T2000Error('INSUFFICIENT_BALANCE', `Insufficient ${asset} to pay`, {\n      available: total.toString(),\n      required: amountRaw.toString(),\n    });\n  }\n\n  // address balance = total − discrete coin-object sum (listCoins excludes AB).\n  // Collect the coin objects too — we reuse them to build the migration.\n  const coins: { objectId: string; balance: bigint }[] = [];\n  let coinSum = 0n;\n  let cursor: string | null | undefined;\n  let hasNext = true;\n  while (hasNext) {\n    const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? undefined });\n    for (const c of page.objects) {\n      coins.push({ objectId: c.objectId, balance: BigInt(c.balance) });\n      coinSum += BigInt(c.balance);\n    }\n    cursor = page.cursor;\n    hasNext = page.hasNextPage;\n  }\n  const addressBalance = total - coinSum;\n  if (addressBalance >= amountRaw) return 0; // address balance already covers it\n\n  // Move the shortfall from coin objects into the address balance by sending\n  // WHOLE coin objects to self via `0x2::coin::send_funds` — one allowlisted\n  // framework MoveCall per coin, NO native merge/split. Built on the gRPC\n  // client so its resolver detects the all-allowlisted-MoveCall shape and zeros\n  // gas: the migration itself is gasless (the same eligibility the x402\n  // withdrawal + the gasless send rely on). The old merge+split+send shape had\n  // native `SplitCoins`/`MergeCoins` commands → fell outside the allowlist →\n  // forced SUI gas, breaking coin-object holders with 0 SUI.\n  const shortfall = amountRaw - addressBalance;\n  const { buildCoinToAddressBalanceMigration } = await import('./coinSelection.js');\n  const grpcClient = await makeGrpcBuildClient(client);\n  const { tx } = buildCoinToAddressBalanceMigration({ coins, coinType: asset, owner, minAmount: shortfall });\n  const migration = await executeTx(client, signer, () => tx, { buildClient: grpcClient });\n  return migration.gasCostSui;\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n/** Read the response body (json or text) and assemble the base PayResult. */\nasync function finalize(response: Response, opts: { paid: boolean }): Promise<PayResult> {\n  const contentType = response.headers.get('content-type') ?? '';\n  let body: unknown;\n  try {\n    body = contentType.includes('application/json') ? await response.json() : await response.text();\n  } catch {\n    body = null;\n  }\n  return { status: response.status, body, paid: opts.paid };\n}\n\n/** A gRPC client for tx BUILD — its resolver auto-detects the gasless\n * stablecoin shape (gasPrice/gasBudget/gasPayment zeroed). */\nasync function makeGrpcBuildClient(client: SuiGrpcClient): Promise<SuiGrpcClient> {\n  const { SuiGrpcClient } = await import('@mysten/sui/grpc');\n  const network: 'mainnet' | 'testnet' = client.network === 'testnet' ? 'testnet' : 'mainnet';\n  const baseUrl =\n    network === 'testnet' ? 'https://fullnode.testnet.sui.io' : 'https://fullnode.mainnet.sui.io';\n  return new SuiGrpcClient({ baseUrl, network });\n}\n\nfunction atomicToHuman(raw: bigint, decimals: number): number {\n  return Number(raw) / 10 ** decimals;\n}\n\nasync function assetDecimals(coinType: string): Promise<number> {\n  try {\n    const { getDecimalsForCoinType } = await import('../token-registry.js');\n    const d = getDecimalsForCoinType(coinType);\n    return typeof d === 'number' ? d : 6;\n  } catch {\n    return 6;\n  }\n}\n","import type { SuiClientTypes } from '@mysten/sui/client';\nimport type { Transaction } from '@mysten/sui/transactions';\nimport type { TransactionSigner } from '../signer.js';\nimport type { SuiCoreClient } from '../utils/sui.js';\n\n// ---------------------------------------------------------------------------\n// executeTx — build + sign + submit + wait, the SDK's one tx-execution helper.\n//\n// Browser-safe (no fs / keyManager imports) so it can back\n// both the Node-side `T2000` methods AND the browser-side `payWithMpp`\n// (gasless MPP runs client-side on the zkLogin session key). Moved out of\n// `t2000.ts` so `wallet/pay.ts` can share it without pulling the Node-only\n// `T2000` module graph into the browser bundle.\n//\n// [gRPC migration] Submit + wait go through the unified `client.core.*`\n// API (`executeTransaction` + `waitForTransaction`), which is transport-\n// agnostic across the JSON-RPC and gRPC clients.\n// ---------------------------------------------------------------------------\n\nexport type SuiTransactionEffects = SuiClientTypes.TransactionEffects;\nexport type BuildClient = NonNullable<Parameters<Transaction['build']>[0]>['client'];\n\nexport async function executeTx(\n  client: SuiCoreClient,\n  signer: TransactionSigner,\n  buildTx: () => Promise<Transaction> | Transaction,\n  options: { buildClient?: BuildClient } = {},\n): Promise<{ digest: string; gasCostSui: number; effects: SuiTransactionEffects | undefined }> {\n  const tx = await buildTx();\n  tx.setSender(signer.getAddress());\n  // [2026-05-22] Optional buildClient. When set, `tx.build()` uses it to\n  // resolve the PTB — relevant for gasless stablecoin transfers where the\n  // SuiGrpcClient build path auto-detects allowlisted ops and zeros out\n  // gasPrice/gasBudget/gasPayment. See `payWithMpp`.\n  const txBytes = await tx.build({ client: options.buildClient ?? client });\n  const { signature } = await signer.signTransaction(txBytes);\n  const result = await client.core.executeTransaction({\n    transaction: txBytes,\n    signatures: [signature],\n    include: { effects: true },\n  });\n  const txn = result.$kind === 'Transaction' ? result.Transaction : result.FailedTransaction;\n  await client.core.waitForTransaction({ digest: txn.digest });\n  const effects = txn.effects ?? undefined;\n  const gasUsed = effects?.gasUsed;\n  let gasCostSui = 0;\n  if (gasUsed) {\n    const total = BigInt(gasUsed.computationCost) + BigInt(gasUsed.storageCost) - BigInt(gasUsed.storageRebate);\n    gasCostSui = Number(total) / 1e9;\n  }\n  return { digest: txn.digest, gasCostSui, effects };\n}\n","// Builder-appropriate safety, layer 2 (SPEC_AUDRIC_V3 §7 / safeguards-defense-\n// in-depth.mdc). `preflight()` is CHEAP, PURE, SYNCHRONOUS input validation —\n// no network, no I/O, no context lookup. It runs BEFORE the LLM round-trip /\n// before the tap-to-confirm card, so a malformed intent is rejected without\n// the user ever seeing a confirm for it.\n//\n// Each write builder co-locates its own `preflight*` validator (send.ts →\n// `preflightSend`, pay.ts → `preflightPay`, cetus-swap.ts → `preflightSwap`).\n// This module owns ONLY the shared result type + the two pure primitives those\n// validators compose (amount sanity + sync address validity). The agent-loop\n// guards that read conversation/session state stay in the v3 HOST (they are\n// NOT builder-appropriate, S.442/S.443).\n\nimport { isValidSuiAddress, normalizeSuiAddress } from '@mysten/sui/utils';\nimport type { T2000ErrorCode } from './errors.js';\n\n/**\n * The result of a synchronous preflight check. `valid: false` carries a\n * `T2000ErrorCode` + human message so the host can surface a precise reason\n * (and the builder can rethrow it as a `T2000Error` verbatim).\n */\nexport type PreflightResult =\n  | { valid: true }\n  | { valid: false; code: T2000ErrorCode; error: string };\n\n/**\n * Fat-finger / overflow ceiling for an asset amount. NOT a spending policy\n * (that's the `@t2000/sdk/limits` module — USD-denominated, opt-in, stateful);\n * this is the \"obviously wrong number\" guard the safeguards rule prescribes\n * (`amount > 1_000_000` → unreasonable).\n */\nexport const PREFLIGHT_MAX_AMOUNT = 1_000_000;\n\nexport const PREFLIGHT_OK: PreflightResult = { valid: true };\n\nexport function preflightFail(code: T2000ErrorCode, error: string): PreflightResult {\n  return { valid: false, code, error };\n}\n\n/**\n * Pure positive-finite-amount sanity check (no network). Rejects NaN/Infinity\n * and non-positive values. The absurd-value ceiling defaults to\n * {@link PREFLIGHT_MAX_AMOUNT} but callers can raise it — pass\n * `max: Number.POSITIVE_INFINITY` for swaps of low-unit-value tokens\n * (memecoins legitimately trade in millions/billions of units), where a fixed\n * display-amount ceiling would be a false positive.\n */\nexport function checkPositiveAmount(\n  amount: number,\n  label = 'Amount',\n  max: number = PREFLIGHT_MAX_AMOUNT,\n): PreflightResult {\n  if (typeof amount !== 'number' || !Number.isFinite(amount)) {\n    return preflightFail('INVALID_AMOUNT', `${label} must be a finite number`);\n  }\n  if (amount <= 0) {\n    return preflightFail('INVALID_AMOUNT', `${label} must be greater than zero`);\n  }\n  if (amount > max) {\n    return preflightFail('INVALID_AMOUNT', `${label} ${amount} exceeds the sane maximum (${max})`);\n  }\n  return PREFLIGHT_OK;\n}\n\n/**\n * Pure, synchronous Sui-address validity (no throw, no network) — the\n * non-throwing sibling of `validateAddress`. Use in preflight; use\n * `validateAddress` when you need the normalized form.\n */\nexport function checkSuiAddress(address: string, label = 'recipient'): PreflightResult {\n  try {\n    // `normalizeSuiAddress('')` pads to the zero address (which is \"valid\"), so\n    // guard empty / whitespace-only input before normalizing.\n    if (typeof address === 'string' && address.trim() !== '' &&\n        isValidSuiAddress(normalizeSuiAddress(address))) {\n      return PREFLIGHT_OK;\n    }\n  } catch {\n    // normalizeSuiAddress throws on malformed input → fall through to the fail.\n  }\n  return preflightFail('INVALID_ADDRESS', `Invalid ${label} address: ${address}`);\n}\n","/**\n * Browser-safe entry point for @t2000/sdk.\n *\n * Exports everything the web app needs WITHOUT Node-only modules\n * (e.g. the fs-based keyManager is excluded).\n */\n\n// Signer abstraction\nexport type { TransactionSigner } from './signer.js';\nexport { KeypairSigner } from './wallet/keypairSigner.js';\nexport { ZkLoginSigner, type ZkLoginProof } from './wallet/zkLoginSigner.js';\n\n// Gasless MPP pay — browser-safe; the Audric client runs this in-browser on\n// the zkLogin session key (unified gasless write path). Same canonical loop\n// `T2000.pay()` delegates to. Pair with `executeTx` for advanced callers.\n// (`PayOptions` / `PayResult` are exported from the types block below.)\nexport { payWithMpp, preflightPay } from './wallet/pay.js';\nexport { executeTx } from './wallet/executeTx.js';\n\n// Error handling\nexport { T2000Error, mapWalletError, mapMoveAbortCode } from './errors.js';\nexport type { T2000ErrorCode, T2000ErrorData } from './errors.js';\n\n// Constants\nexport {\n  MIST_PER_SUI,\n  SUI_DECIMALS,\n  USDC_DECIMALS,\n  T2000_OVERLAY_FEE_WALLET,\n  SUPPORTED_ASSETS,\n  CLOCK_ID,\n  DEFAULT_NETWORK,\n} from './constants.js';\nexport type { SupportedAsset, StableAsset } from './constants.js';\nexport {\n  STABLE_ASSETS,\n  GAS_RESERVE_MIN,\n} from './constants.js';\n\n// Utilities\nexport { validateAddress, truncateAddress } from './utils/sui.js';\nexport {\n  KNOWN_TARGETS,\n  LABEL_PATTERNS,\n  classifyAction,\n  classifyLabel,\n  fallbackLabel,\n  refineLendingLabel,\n  classifyTransaction,\n  extractTransferDetails,\n  extractAllUserLegs,\n} from './wallet/classify.js';\nexport type {\n  ClassifyBalanceChange,\n  ClassifyResult,\n  ExtractedTransfer,\n  TxDirection,\n} from './wallet/classify.js';\n/**\n * Tx parsing helpers. Safe in the browser — they only do shape\n * inspection / classification and do not import any Node-only modules.\n * `queryHistory` and `queryTransaction` are not re-exported here\n * because they reach the network (GraphQL) directly; browser consumers\n * can build the same flow with `parseSuiRpcTx` + their own fetch.\n */\nexport {\n  parseSuiRpcTx,\n  extractTxSender,\n  extractTxCommands,\n} from './wallet/history.js';\nexport type { SuiRpcTxBlock } from './wallet/history.js';\nexport {\n  mistToSui,\n  suiToMist,\n  usdcToRaw,\n  rawToUsdc,\n  stableToRaw,\n  rawToStable,\n  getDecimals,\n  formatUsd,\n  formatSui,\n  formatAssetAmount,\n} from './utils/format.js';\nexport { toBase64, fromBase64 } from './utils/base64.js';\n\n// Simulation — use dynamic import() to avoid Buffer dependency in browser bundles\n// import { simulateTransaction } from '@t2000/sdk' (main entry) for Node usage\nexport type { SimulationResult } from './utils/simulate.js';\n\n// Protocol fee\n// Cetus aggregator helpers — Audric prepare/route.ts uses these directly to\n// build swap PTBs with overlay fees (per-call, not module-global).\nexport { findSwapRoute, buildSwapTx, OVERLAY_FEE_RATE, preflightSwap } from './protocols/cetus-swap.js';\nexport type { SwapRouteResult, OverlayFeeConfig } from './protocols/cetus-swap.js';\n\n// Synchronous, network-free preflight (layer 2) — pure, browser-safe. The v3\n// host runs these in the agent loop before the tap-to-confirm card.\n// buildSendTx is browser-safe (builds a gasless PTB; the client is injected) —\n// the Audric client signs it in-browser with the zkLogin session (send_transfer).\nexport { buildSendTx, preflightSend } from './wallet/send.js';\nexport {\n  type PreflightResult,\n  PREFLIGHT_MAX_AMOUNT,\n  PREFLIGHT_OK,\n  preflightFail,\n  checkPositiveAmount,\n  checkSuiAddress,\n} from './preflight.js';\n\n// Spending limits are Node-only (`@t2000/sdk/limits` uses node:fs) — NOT\n// exported here. The browser (Audric) write path skips client-side limits;\n// the server budget ledger is the cap there.\n\n// Types\nexport type {\n  BalanceResponse,\n  SuiHolding,\n  SendResult,\n  DepositInfo,\n  TransactionRecord,\n  TransactionLeg,\n  PayOptions,\n  PayResult,\n} from './types.js';\n\n// Token registry — zero Node.js deps, safe for client-side use\nexport {\n  COIN_REGISTRY,\n  TOKEN_MAP,\n  resolveTokenType,\n  resolveSymbol,\n  getDecimalsForCoinType,\n  SUI_TYPE,\n  USDC_TYPE,\n  USDT_TYPE,\n  USDSUI_TYPE,\n  USDE_TYPE,\n  ETH_TYPE,\n  WBTC_TYPE,\n  WAL_TYPE,\n  NAVX_TYPE,\n  IKA_TYPE,\n  LOFI_TYPE,\n  MANIFEST_TYPE,\n} from './token-registry.js';\nexport type { CoinMeta } from './token-registry.js';\n","import { T2000Error } from './errors.js';\n\nexport const MIST_PER_SUI = 1_000_000_000n;\nexport const SUI_DECIMALS = 9;\nexport const USDC_DECIMALS = 6;\n\nexport const PRECISION = 1_000_000_000_000_000_000n;\n\nexport const MIN_DEPOSIT = 1_000_000n; // 1 USDC (6 decimals)\n\nexport const CLOCK_ID = '0x6';\n\nexport const SUPPORTED_ASSETS = {\n  USDC: {\n    type: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',\n    decimals: 6,\n    symbol: 'USDC',\n    displayName: 'USDC',\n  },\n  USDT: {\n    type: '0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT',\n    decimals: 6,\n    symbol: 'USDT',\n    displayName: 'suiUSDT',\n  },\n  USDe: {\n    type: '0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE',\n    decimals: 6,\n    symbol: 'USDe',\n    displayName: 'suiUSDe',\n  },\n  USDsui: {\n    type: '0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI',\n    decimals: 6,\n    symbol: 'USDsui',\n    displayName: 'USDsui',\n  },\n  SUI: {\n    type: '0x2::sui::SUI',\n    decimals: 9,\n    symbol: 'SUI',\n    displayName: 'SUI',\n  },\n  WAL: {\n    type: '0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL',\n    decimals: 9,\n    symbol: 'WAL',\n    displayName: 'WAL',\n  },\n  ETH: {\n    type: '0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH',\n    decimals: 8,\n    symbol: 'ETH',\n    displayName: 'suiETH',\n  },\n  NAVX: {\n    type: '0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX',\n    decimals: 9,\n    symbol: 'NAVX',\n    displayName: 'NAVX',\n  },\n  GOLD: {\n    type: '0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM',\n    decimals: 6,\n    symbol: 'GOLD',\n    displayName: 'XAUM',\n  },\n} as const;\n\nexport type SupportedAsset = keyof typeof SUPPORTED_ASSETS;\n\n// [SPEC_AGENTIC_STACK P1 / SDK F3 — 2026-05-25]\n// `StableAsset` = \"what we treat as worth $1 for wallet pricing and balance roll-up\".\n// Pre-Phase 1: USDC only — left USDsui wallet holdings invisible to balance.ts.\n// Phase 1: widened to USDC + USDsui (both are NAVI-native stables with $1 peg).\n// USDT / USDe stay OUT of this set deliberately — they are stables, but the\n// codebase has never priced them at $1, and adding them here would require\n// auditing balance.ts callers downstream. Keep the carve minimal.\nexport type StableAsset = 'USDC' | 'USDsui';\nexport const STABLE_ASSETS: readonly StableAsset[] = ['USDC', 'USDsui'] as const;\n\n// ---------------------------------------------------------------------------\n// Operation → allowed asset rules (single source of truth)\n// ---------------------------------------------------------------------------\n\n// [v4.0 Phase A Day 2 — 2026-05-26] `send` is constrained to\n// `['USDC', 'USDsui', 'SUI']`. Rationale (SPEC_AGENT_WALLET_GREENFIELD §A):\n// - USDC + USDsui are gasless via `0x2::balance::send_funds` (Sui mainnet\n//   protocol allowlist) — the two foundation stables.\n// - SUI is the only non-stable kept on the allowlist so users with no\n//   stablecoin balance can still pay gas-native SUI transfers.\n// - USDT, USDe, WAL, ETH, NAVX, GOLD are NOT sendable — users must swap to\n//   USDC/USDsui first (one-step) or hold SUI and use a manual Move call.\n// `swap` is unrestricted (Cetus routes any pair). The DeFi operations\n// (save/borrow/withdraw/repay) were removed with NAVI.\nexport const OPERATION_ASSETS = {\n  send: ['USDC', 'USDsui', 'SUI'],\n  swap: '*',\n} as const;\n\nexport type Operation = keyof typeof OPERATION_ASSETS;\n\nexport function isAllowedAsset(op: Operation, asset: string): boolean {\n  const allowed = OPERATION_ASSETS[op];\n  if (allowed === '*') return true;\n  // [v0.51.0] Mixed-case canonical keys (USDsui, suiUSDT) need case-insensitive\n  // membership. Pre-v0.51 we only had USDC ↔ USDC (uppercase identity), so\n  // a one-sided uppercase compare looked correct. Now that USDsui is in the\n  // set, normalize both sides.\n  const target = asset.toLowerCase();\n  return (allowed as readonly string[]).some((a) => a.toLowerCase() === target);\n}\n\n/**\n * Throws if the asset is not permitted for the given operation.\n *\n * [v4.0 Phase A Day 2] Pre-v4 this allowed `undefined` as a silent default\n * to USDC. Removed because every write path now requires explicit asset\n * (see `T2000.send` + `buildSendTx` + `composeTx.send_transfer`). The\n * `undefined → no-op` branch is kept defensively; the `send_transfer` flow\n * validates non-undefined.\n */\nexport function assertAllowedAsset(op: Operation, asset: string | undefined): void {\n  if (!asset) return;\n  if (!isAllowedAsset(op, asset)) {\n    const allowed = OPERATION_ASSETS[op];\n    const list = Array.isArray(allowed) ? allowed.join(', ') : 'any';\n    const swapHint =\n      op === 'send' ? ' Swap to USDC or USDsui first, or send SUI.' : '';\n    throw new T2000Error(\n      'INVALID_ASSET',\n      `${op} only supports ${list}. Cannot use ${asset}.${swapHint}`,\n    );\n  }\n}\n\n/**\n * [v4.0 Phase A Day 2] Narrow type alias for assets sendable through the\n * Agent Wallet. Matches `OPERATION_ASSETS.send` exactly. Exported so the\n * CLI / SDK / composeTx can share one type without re-declaring it.\n */\nexport type SendableAsset = 'USDC' | 'USDsui' | 'SUI';\nexport const SENDABLE_ASSETS: readonly SendableAsset[] = ['USDC', 'USDsui', 'SUI'] as const;\n\n/**\n * [v4.0 Phase A Day 2] Coin types for the two gasless-allowlisted stables.\n * Used by `wallet/send.ts` + `composeTx.send_transfer` to construct the\n * `0x2::balance::send_funds` Move call's `typeArguments`. SUI is excluded\n * because SUI transfers are NOT gasless (gas-native, uses `tx.gas` split +\n * `transferObjects` per the existing path).\n */\nexport const GASLESS_STABLE_TYPES: Record<'USDC' | 'USDsui', string> = {\n  USDC: SUPPORTED_ASSETS.USDC.type,\n  USDsui: SUPPORTED_ASSETS.USDsui.type,\n};\n\n// Swap overlay fees route here. Audric's prepare/route.ts passes\n// `overlayFee.receiver = T2000_OVERLAY_FEE_WALLET` to the Cetus aggregator. The\n// CLI/SDK never charge fees — this constant is exported for consumer apps only.\n//\n// Address corresponds to the treasury admin wallet. Override via env for local dev /\n// testnet only — production must use the canonical mainnet address below.\nexport const T2000_OVERLAY_FEE_WALLET = process.env.T2000_OVERLAY_FEE_WALLET\n  ?? '0x5366efbf2b4fe5767fe2e78eb197aa5f5d138d88ac3333fbf3f80a1927da473a';\n\nexport const DEFAULT_NETWORK = 'mainnet' as const;\n// gRPC fullnode endpoint. Post-flip this is the canonical transport for the\n// whole SDK: `getSuiClient()` (reads + execution) AND `getSuiGrpcClient()`\n// (gasless stablecoin build detection) both target it. Override the read/exec\n// client with T2000_RPC_URL and the build client with T2000_GRPC_URL.\nexport const DEFAULT_RPC_URL = 'https://fullnode.mainnet.sui.io:443';\nexport const DEFAULT_GRPC_URL = 'https://fullnode.mainnet.sui.io:443';\n// [gRPC migration] GraphQL endpoint for the query surface that has NO gRPC\n// `core` equivalent — `transactionBlocks` (history). Used by\n// `getSuiGraphQLClient()`. The canonical mainnet host is `graphql.mainnet.sui.io`\n// (parallel to the gRPC fullnode); the older `sui-mainnet.mystenlabs.com/graphql`\n// host was dropped 2026-06-15 after it began resetting TLS connections (live\n// smoke). Public endpoint is rate-limited — production hosts override with\n// T2000_GRAPHQL_URL to a dedicated provider.\nexport const DEFAULT_GRAPHQL_URL = 'https://graphql.mainnet.sui.io/graphql';\nexport const DEFAULT_KEY_PATH = '~/.t2000/wallet.key';\nexport const DEFAULT_CONFIG_PATH = '~/.t2000/config.json';\n\n// [v4.0 Phase A Day 2] Minimum stablecoin amount for protocol-level gasless\n// transfers via `0x2::balance::send_funds`. The Sui mainnet allowlist enforces\n// this floor at the protocol level to prevent dust spam; we surface a clear\n// error before signing rather than letting the tx revert on-chain.\nexport const GASLESS_MIN_STABLE_AMOUNT = 0.01;\n\n// Cetus USDC/SUI pool — read-only for SUI price oracle (no SDK dependency)\nexport const CETUS_USDC_SUI_POOL = '0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab';\n\nexport const GAS_RESERVE_MIN = 0.05; // minimum SUI to keep for gas\n","import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { SuiGraphQLClient } from '@mysten/sui/graphql';\nimport { isValidSuiAddress, normalizeSuiAddress } from '@mysten/sui/utils';\nimport { DEFAULT_GRAPHQL_URL, DEFAULT_GRPC_URL, DEFAULT_RPC_URL } from '../constants.js';\nimport { T2000Error } from '../errors.js';\n\n/**\n * [gRPC migration / S.438] Transport-agnostic client type for the migration.\n *\n * Both `SuiJsonRpcClient` and `SuiGrpcClient` extend `BaseClient` and expose\n * an identical `.core.*` API. During the migration, call sites are rewritten\n * from legacy methods (`getBalance`, `getObject`, `executeTransactionBlock`)\n * to the unified `client.core.*` API and typed against this union — so the\n * rewrite is behavior-preserving while still on JSON-RPC, and the final\n * transport flip (gRPC) is a one-line change in `getSuiClient()`.\n *\n * The query surface that has no gRPC `core` equivalent (`queryTransactionBlocks`\n * / `queryEvents`, Stage 0 finding A) uses `getSuiGraphQLClient()` instead.\n */\nexport type SuiCoreClient = SuiJsonRpcClient | SuiGrpcClient;\n\n/**\n * Resolve the effective JSON-RPC URL: explicit arg > env var > default.\n * `T2000_RPC_URL` is an OPTIONAL override per the Greenfield SPEC's env\n * contract — no required vars, so we read inline (allowed by\n * `env-validation-gate.mdc` carve-out for packages with zero required\n * env vars).\n */\nfunction resolveRpcUrl(rpcUrl?: string): string {\n  if (rpcUrl) return rpcUrl;\n  const envUrl = process.env.T2000_RPC_URL?.trim();\n  if (envUrl) return envUrl;\n  return DEFAULT_RPC_URL;\n}\n\n/**\n * Same shape as `resolveRpcUrl` for the gRPC endpoint.\n */\nfunction resolveGrpcUrl(grpcUrl?: string): string {\n  if (grpcUrl) return grpcUrl;\n  const envUrl = process.env.T2000_GRPC_URL?.trim();\n  if (envUrl) return envUrl;\n  return DEFAULT_GRPC_URL;\n}\n\n/**\n * Same shape as `resolveRpcUrl` for the GraphQL endpoint.\n */\nfunction resolveGraphqlUrl(graphqlUrl?: string): string {\n  if (graphqlUrl) return graphqlUrl;\n  const envUrl = process.env.T2000_GRAPHQL_URL?.trim();\n  if (envUrl) return envUrl;\n  return DEFAULT_GRAPHQL_URL;\n}\n\n/**\n * Canonical agent client cache, keyed by URL. The earlier impl cached\n * unconditionally and ignored URL changes after the first call —\n * brittle for testnet smokes and custom-RPC test setups.\n */\nconst rpcClientCache = new Map<string, SuiGrpcClient>();\n\n/**\n * [gRPC cutover] The canonical agent client is now gRPC. Mysten deactivates\n * the JSON-RPC fullnode interface on 2026-07-31, so reads (`.core.*`) and\n * execution route over gRPC — transaction *builds* already do\n * (`getSuiGrpcClient`). The whole SDK was first refactored onto the unified\n * `.core` API (behavior-preserving on JSON-RPC), then flipped here. The\n * `rpcUrl` arg / `T2000_RPC_URL` override now resolve a gRPC `baseUrl`\n * (`DEFAULT_RPC_URL` is the same host as `DEFAULT_GRPC_URL`). The historical\n * query surface stays on `getSuiGraphQLClient()`.\n */\nexport function getSuiClient(rpcUrl?: string): SuiGrpcClient {\n  const url = resolveRpcUrl(rpcUrl);\n  const cached = rpcClientCache.get(url);\n  if (cached) return cached;\n  const client = new SuiGrpcClient({ baseUrl: url, network: 'mainnet' });\n  rpcClientCache.set(url, client);\n  return client;\n}\n\n/**\n * gRPC client cache, keyed by URL. Same rationale as the JSON-RPC cache:\n * different URLs must produce different clients.\n */\nconst grpcClientCache = new Map<string, SuiGrpcClient>();\n\n/**\n * Cached `SuiGrpcClient` for gasless stablecoin transfer builds.\n *\n * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A]\n *\n * Why this exists: Sui mainnet's protocol-level gasless stablecoin transfers\n * (`0x2::balance::send_funds` on the USDC + USDsui allowlist) are detected\n * ONLY when the transaction is built through a `SuiGrpcClient`. The gRPC\n * client's build resolver inspects the PTB at `tx.build()` time and, if it\n * matches the gasless pattern, sets `gasPrice=0` + `gasBudget=0` automatically.\n * Building the SAME PTB through `SuiJsonRpcClient` produces the same bytes\n * but with non-zero gas — the tx still works, but the user pays SUI gas.\n *\n * Execution stays on JSON-RPC (`SuiJsonRpcClient.executeTransactionBlock`)\n * because (a) the rest of the SDK expects JSON-RPC and (b) Sui's docs\n * explicitly support a \"build via gRPC, execute via JSON-RPC\" hybrid:\n * https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers\n *\n * Override the endpoint with the `T2000_GRPC_URL` env var or the\n * `grpcUrl` arg. Cache is keyed by URL so multiple endpoints can\n * co-exist (e.g., the rare testnet smoke + production usage from the\n * same process).\n */\nexport function getSuiGrpcClient(grpcUrl?: string): SuiGrpcClient {\n  const baseUrl = resolveGrpcUrl(grpcUrl);\n  const cached = grpcClientCache.get(baseUrl);\n  if (cached) return cached;\n  const client = new SuiGrpcClient({ baseUrl, network: 'mainnet' });\n  grpcClientCache.set(baseUrl, client);\n  return client;\n}\n\n/**\n * GraphQL client cache, keyed by URL. Same rationale as the other caches.\n */\nconst graphqlClientCache = new Map<string, SuiGraphQLClient>();\n\n/**\n * Cached `SuiGraphQLClient` for the query surface that has NO gRPC `core`\n * equivalent: `queryTransactionBlocks` + `queryEvents` (Stage 0 finding A —\n * `SPEC_FULL_GRPC_MIGRATION.md`). Mysten's gRPC successor doesn't cover the\n * historical query/event API; GraphQL is its documented home. Consumers:\n * `wallet/history.ts` + the swap-event read in `protocols/cetus-swap.ts`.\n *\n * Override the endpoint with `T2000_GRAPHQL_URL` or the `graphqlUrl` arg.\n */\nexport function getSuiGraphQLClient(graphqlUrl?: string): SuiGraphQLClient {\n  const url = resolveGraphqlUrl(graphqlUrl);\n  const cached = graphqlClientCache.get(url);\n  if (cached) return cached;\n  const client = new SuiGraphQLClient({ url, network: 'mainnet' });\n  graphqlClientCache.set(url, client);\n  return client;\n}\n\nexport function validateAddress(address: string): string {\n  const normalized = normalizeSuiAddress(address);\n  if (!isValidSuiAddress(normalized)) {\n    throw new T2000Error('INVALID_ADDRESS', `Invalid Sui address: ${address}`);\n  }\n  return normalized;\n}\n\nexport function truncateAddress(address: string): string {\n  if (address.length <= 10) return address;\n  return `${address.slice(0, 6)}...${address.slice(-4)}`;\n}\n\n/**\n * Normalize a Sui coin type to its canonical long-form 64-hex address.\n * `0x2::sui::SUI` → `0x0000…0002::sui::SUI`. Idempotent on already-long\n * forms. Returns the input unchanged if it doesn't look like a coin type\n * (`<address>::<module>::<name>`) so callers can pass arbitrary strings\n * without crashing.\n *\n * Why this exists: BlockVision's `/v2/sui/coin/price/list` endpoint\n * silently returns an empty `prices` map for short-form coin types\n * (notably `0x2::sui::SUI` — the native gas coin). Internal callers must\n * pass the long form, but external callers (LLM tool args, cached\n * coin-type strings, audit logs) commonly use the short form. Normalize\n * before the network call, denormalize back to the caller's input shape\n * after, and short/long become interchangeable.\n */\nexport function normalizeCoinType(coinType: string): string {\n  const parts = coinType.split('::');\n  if (parts.length !== 3) return coinType;\n  const [addr, mod, name] = parts;\n  if (!addr.startsWith('0x')) return coinType;\n  return `${normalizeSuiAddress(addr)}::${mod}::${name}`;\n}\n","/**\n * Shared transaction classifier.\n *\n * Consumed by both the SDK's `parseTxRecord` (production agent path) and\n * the engine's `transaction_history` tool (cold-start RPC path). Keeping\n * a single source of truth here prevents the two paths from drifting —\n * see v1.5.3 regression where the SDK path was emitting `action:\n * 'transaction'` (rendered as \"On-chain\") while the engine path was\n * already producing fine-grained labels.\n */\n\nimport { getDecimalsForCoinType, resolveSymbol, SUI_TYPE } from '../token-registry.js';\n\n/**\n * Coarse action bucket — one of `'send' | 'lending' | 'swap' |\n * 'transaction'`. Used by the ACI `action` filter on the\n * `transaction_history` tool. STABLE: downstream queries depend on\n * exactly these values.\n *\n * Order matters: more specific buckets first. Lending patterns precede\n * swap patterns so a NAVI `::swap` helper (if one ever existed) would\n * still bucket as lending.\n */\nexport const KNOWN_TARGETS: readonly [RegExp, string][] = [\n  [/::suilend|::obligation/, 'lending'],\n  [/::navi|::lending_core|::incentive_v\\d+|::oracle_pro/, 'lending'],\n  /**\n   * DEX modules — both direct calls and aggregator legs. The Cetus\n   * aggregator dispatches through a per-DEX module (e.g.\n   * `cetus::swap`, `flowx_amm::swap`, `aftermath::swap`, …) plus\n   * router glue functions. We list every DEX module the aggregator\n   * supports today so a single-DEX call still classifies cleanly.\n   */\n  [/::cetus(?:_dlmm)?::|::pool::|::deepbook|::flowx_(?:amm|clmm)::|::kriya_(?:amm|clmm)::|::turbos::|::aftermath::|::afsui::|::bluefin::|::bluemove::|::ferra_(?:clmm|dlmm)::|::haedal_hmm::|::hasui::|::hawal::|::magma::|::momentum::|::obric::|::springsui::|::steamm_cpmm::|::fullsail::|::alphafi::|::volo_swap::/, 'swap'],\n  /**\n   * Cetus aggregator router glue. These are the swap-context and\n   * balance-handling helpers the aggregator emits around per-DEX\n   * legs. Without this entry a tx that ONLY had router calls\n   * (theoretically possible for setup/cleanup) would slip through;\n   * in practice these always coexist with a DEX leg, but the entry\n   * is cheap insurance.\n   */\n  [/::router::(?:new_swap_context(?:_v)?|confirm_swap|transfer_balance|take_balance|transfer_or_destroy_coin)/, 'swap'],\n  [/::transfer::public_transfer/, 'send'],\n];\n\n/**\n * Finer-grained display labels — derived from MoveCall function names.\n * The card renders `label ?? action`, so when this map matches we get\n * \"Deposit\" / \"Withdraw\" / \"Borrow\" / \"Repay\" / \"Payment link\" instead\n * of the generic \"Lending\" or \"Transaction\".\n *\n * Order matters: more specific patterns first. Each entry is\n * (regex, label) where the regex is matched against the\n * fully-qualified MoveCall target `pkg::module::function`.\n */\nexport const LABEL_PATTERNS: readonly [RegExp, string][] = [\n  [/::pay(?:ment_kit|_kit)?::|::create_payment_link|::pay_link/, 'payment_link'],\n  [/::create_invoice|::invoice::/, 'invoice'],\n  [/::deposit|::supply|::mint_ctokens/, 'deposit'],\n  [/::withdraw|::redeem|::redeem_ctokens/, 'withdraw'],\n  [/::borrow/, 'borrow'],\n  [/::repay/, 'repay'],\n  [/::claim_reward|::claim::|::claim_incentive/, 'claim'],\n  [/::stake/, 'stake'],\n  [/::unstake|::burn::/, 'unstake'],\n  [/::liquidate/, 'liquidate'],\n];\n\nexport interface ClassifyBalanceChange {\n  owner: { AddressOwner?: string } | string;\n  coinType: string;\n  amount: string;\n}\n\nfunction resolveOwner(owner: ClassifyBalanceChange['owner']): string | null {\n  if (typeof owner === 'object' && owner.AddressOwner) return owner.AddressOwner;\n  if (typeof owner === 'string') return owner;\n  return null;\n}\n\nexport function classifyAction(targets: string[], commandTypes: string[]): string {\n  for (const target of targets) {\n    for (const [pattern, label] of KNOWN_TARGETS) {\n      if (pattern.test(target)) return label;\n    }\n  }\n  if (commandTypes.includes('TransferObjects') && !commandTypes.includes('MoveCall')) return 'send';\n  return 'transaction';\n}\n\n/**\n * Last-resort fallback when neither `LABEL_PATTERNS` nor the action\n * bucket produces something useful.\n *\n * Returns the first MoveCall's *module* name (e.g. \"navi\", \"spam\") so\n * the card shows something better than the literal word \"transaction\".\n * When no MoveCall exists, returns 'on-chain'.\n *\n * Note: callers should prefer `classifyLabel` which now layers\n * pattern-match → coarse action → module name (see commentary there).\n */\nexport function fallbackLabel(targets: string[]): string {\n  if (!targets.length) return 'on-chain';\n  const first = targets[0];\n  const parts = first.split('::');\n  if (parts.length >= 2 && parts[1]) return parts[1].toLowerCase();\n  return 'on-chain';\n}\n\n/**\n * Three-tier label resolution for the transaction history card:\n *   1. Specific keyword match in `LABEL_PATTERNS` (\"deposit\",\n *      \"payment_link\", …).\n *   2. Coarse action bucket from `classifyAction` (\"swap\", \"send\",\n *      \"lending\") — prevents leaking opaque internal module names like\n *      \"router\" (Cetus aggregator) or \"cross_swap\" (third-party DEX\n *      aggregators) for txs that we already classified as a swap.\n *   3. Module name from the first MoveCall (`fallbackLabel`) — only\n *      used when the action bucket itself is the generic \"transaction\".\n *\n * Pre-v0.46.2 we skipped tier 2, so swaps showed labels like \"router\",\n * \"cross_swap\", \"scallop_router\", etc. instead of the clean \"swap\".\n */\nexport function classifyLabel(targets: string[], commandTypes: string[]): string {\n  for (const target of targets) {\n    for (const [pattern, label] of LABEL_PATTERNS) {\n      if (pattern.test(target)) return label;\n    }\n  }\n  if (commandTypes.includes('TransferObjects') && !commandTypes.includes('MoveCall')) return 'send';\n  const action = classifyAction(targets, commandTypes);\n  if (action !== 'transaction') return action;\n  return fallbackLabel(targets);\n}\n\n/**\n * Balance-direction tiebreaker for ambiguous lending calls.\n *\n * Many lending modules expose generic entry points (NAVI's bundled\n * flash actions, `lending_core::*::entry_*`, etc.) that don't carry\n * a `deposit`/`withdraw`/`borrow`/`repay` keyword in the function\n * name. When `classifyLabel` falls back to a bare module name like\n * `\"lending\"` for a known lending tx, infer direction from the user's\n * non-SUI balance change:\n *   - net outflow of the supplied asset → deposit (also covers repay,\n *     but repay-without-keyword is essentially never emitted).\n *   - net inflow of the supplied asset → withdraw (also covers borrow).\n * SUI is excluded so gas-only transactions don't get mislabeled.\n *\n * If `LABEL_PATTERNS` matched a specific keyword, the existing label is\n * returned unchanged.\n */\nexport function refineLendingLabel(\n  currentAction: string,\n  currentLabel: string,\n  moveCallTargets: string[],\n  changes: ClassifyBalanceChange[],\n  address: string,\n): string {\n  if (currentAction !== 'lending') return currentLabel;\n  const labelMatchedSpecific = LABEL_PATTERNS.some(([p]) =>\n    moveCallTargets.some((t) => p.test(t)),\n  );\n  if (labelMatchedSpecific) return currentLabel;\n\n  const userNonSuiOutflow = changes.find(\n    (c) => resolveOwner(c.owner) === address && c.coinType !== SUI_TYPE && BigInt(c.amount) < 0n,\n  );\n  if (userNonSuiOutflow) return 'deposit';\n\n  const userNonSuiInflow = changes.find(\n    (c) => resolveOwner(c.owner) === address && c.coinType !== SUI_TYPE && BigInt(c.amount) > 0n,\n  );\n  if (userNonSuiInflow) return 'withdraw';\n\n  return currentLabel;\n}\n\nexport interface ClassifyResult {\n  action: string;\n  label: string;\n}\n\nexport function classifyTransaction(\n  moveCallTargets: string[],\n  commandTypes: string[],\n  balanceChanges: ClassifyBalanceChange[],\n  address: string,\n): ClassifyResult {\n  const action = classifyAction(moveCallTargets, commandTypes);\n  const baseLabel = classifyLabel(moveCallTargets, commandTypes);\n  const label = refineLendingLabel(action, baseLabel, moveCallTargets, balanceChanges, address);\n  return { action, label };\n}\n\n/**\n * Direction of the user's net non-gas movement for this transaction.\n *\n *   - `'out'` — the user spent the asset (sends, deposits, repays,\n *     swap-in, payment-link payouts).\n *   - `'in'`  — the user received the asset (withdraws, borrows,\n *     swap-out, claims, deposits credited from another wallet).\n *\n * Used by the `TransactionHistoryCard` to choose the `+`/`−` sign and\n * color. Direction is computed from the actual on-chain balance change\n * — never from the textual label — so opaque action types (`'router'`,\n * `'cross_swap'`, …) still render the correct sign.\n */\nexport type TxDirection = 'in' | 'out';\n\nexport interface ExtractedTransfer {\n  amount?: number;\n  asset?: string;\n  recipient?: string;\n  direction?: TxDirection;\n}\n\n/**\n * Extracts the principal amount/asset/direction for a transaction\n * from its `balanceChanges`.\n *\n * Algorithm:\n *   1. Restrict to the user's *own* balance changes.\n *   2. Prefer non-SUI changes (gas-only SUI deltas are noise).\n *   3. Pick the change with the largest absolute value — the \"principal\".\n *   4. If no non-SUI change exists, fall back to the largest SUI change\n *      so pure-SUI transfers (stake/unstake/native send) still render.\n *   5. Direction follows the sign of the principal.\n *   6. Recipient is set only on outflows, by finding a matching inflow\n *      on a *non-user* address with the same coinType.\n *\n * Pre-v0.46.2 this function only inspected outflows, so withdraws,\n * borrows, claims, swap-receives and payment-link receives all\n * rendered with no amount on the rich card (and with a wrong sign,\n * because the card guessed direction from the label string).\n */\nexport function extractTransferDetails(\n  changes: ClassifyBalanceChange[] | undefined,\n  sender: string,\n): ExtractedTransfer {\n  if (!changes || changes.length === 0) return {};\n\n  const userChanges = changes.filter((c) => resolveOwner(c.owner) === sender);\n  if (userChanges.length === 0) return {};\n\n  const userNonSui = userChanges.filter((c) => c.coinType !== SUI_TYPE);\n  const pool = userNonSui.length > 0 ? userNonSui : userChanges;\n\n  let primary = pool[0];\n  let primaryAbs = bigintAbs(BigInt(primary.amount));\n  for (let i = 1; i < pool.length; i++) {\n    const abs = bigintAbs(BigInt(pool[i].amount));\n    if (abs > primaryAbs) {\n      primary = pool[i];\n      primaryAbs = abs;\n    }\n  }\n\n  const raw = BigInt(primary.amount);\n  if (raw === 0n) return {};\n\n  const decimals = getDecimalsForCoinType(primary.coinType);\n  const amount = Number(primaryAbs) / 10 ** decimals;\n  const asset = resolveSymbol(primary.coinType);\n  const direction: TxDirection = raw < 0n ? 'out' : 'in';\n\n  let recipient: string | undefined;\n  if (direction === 'out') {\n    const recipientChange = changes.find(\n      (c) =>\n        resolveOwner(c.owner) !== sender &&\n        c.coinType === primary.coinType &&\n        BigInt(c.amount) > 0n,\n    );\n    recipient = recipientChange ? resolveOwner(recipientChange.owner) ?? undefined : undefined;\n  }\n\n  return { amount, asset, recipient, direction };\n}\n\nfunction bigintAbs(n: bigint): bigint {\n  return n < 0n ? -n : n;\n}\n\n/**\n * Extract every non-zero user balance leg for a transaction — not\n * just the largest one. Order is RPC order; callers responsible for\n * any sorting (e.g. audric sorts by USD value once it's priced).\n *\n * Sui collapses balance changes by coin type, so a 3-step bundle\n * touching USDC three times surfaces as ONE leg of net USDC delta.\n * Distinguishing per-step legs would require parsing the PTB's\n * commands; this helper deliberately stops at the balance-change\n * granularity because that's what's reliably available across all\n * Sui RPC versions.\n *\n * Pre-v1.27.2 the only public API was `extractTransferDetails`,\n * which returned a single \"primary\" leg and made swap rows\n * unrenderable (showed `Swapped 987.60 MANIFEST` because MANIFEST\n * was the largest raw delta even though USDC was the value side).\n */\nexport function extractAllUserLegs(\n  changes: ClassifyBalanceChange[] | undefined,\n  sender: string,\n): import('../types.js').TransactionLeg[] {\n  if (!changes || changes.length === 0) return [];\n\n  const legs: import('../types.js').TransactionLeg[] = [];\n  for (const c of changes) {\n    if (resolveOwner(c.owner) !== sender) continue;\n    const raw = BigInt(c.amount);\n    if (raw === 0n) continue;\n    const decimals = getDecimalsForCoinType(c.coinType);\n    const absAmount = bigintAbs(raw);\n    legs.push({\n      coinType: c.coinType,\n      asset: resolveSymbol(c.coinType),\n      decimals,\n      amount: Number(absAmount) / 10 ** decimals,\n      rawAmount: raw.toString(),\n      direction: raw < 0n ? 'out' : 'in',\n    });\n  }\n\n  return legs;\n}\n","import type { TransactionRecord } from '../types.js';\nimport { getSuiGraphQLClient } from '../utils/sui.js';\nimport {\n  classifyTransaction,\n  extractAllUserLegs,\n  extractTransferDetails,\n  type ClassifyBalanceChange,\n} from './classify.js';\n\n// ---------------------------------------------------------------------------\n// [gRPC migration / S.447 + S.450] Transaction history is the one surface with\n// NO gRPC `core.*` equivalent (Stage 0 finding A): list-by-affected-address +\n// per-tx-by-digest live in the GraphQL RPC, not gRPC. Both go through the\n// Sui GraphQL endpoint (`getSuiGraphQLClient()`), share one node fragment +\n// one mapper, and feed the existing (tested) classifier in `classify.ts`.\n//\n// LIVE-VERIFIED 2026-06-15 (S.450) against `graphql.mainnet.sui.io` with a\n// real sender (send + Cetus swap digests). The live schema differs from the\n// older `transactionBlocks` one: the query is `transactions` / `transaction`\n// (filter `affectedAddress` — sender, sponsor, or recipient), the programmable kind is `ProgrammableTransaction`\n// (was `ProgrammableTransactionBlock`) with `commands { nodes }` (was\n// `transactions`), and a move call is `MoveCallCommand` with a nested\n// `function { name module { name package { address } } }` (was a flat\n// `MoveCallTransaction { package module functionName }`). GraphQL errors are\n// now SURFACED, not swallowed (the old `?? []` hid this schema drift as an\n// empty history for a whole session). Schema ref: https://docs.sui.io/references/sui-graphql\n// ---------------------------------------------------------------------------\n\nconst TX_NODE_FRAGMENT = `\n  digest\n  effects {\n    timestamp\n    gasEffects { gasSummary { computationCost storageCost storageRebate } }\n    balanceChanges { nodes { amount coinType { repr } owner { address } } }\n  }\n  kind {\n    __typename\n    ... on ProgrammableTransaction {\n      commands {\n        nodes {\n          __typename\n          ... on MoveCallCommand { function { name module { name package { address } } } }\n        }\n      }\n    }\n  }\n`;\n\n// `affectedAddress` (not `sentAddress`) so history includes INCOMING transactions\n// — received payments, x402/MPP refunds, airdrops — not just ones the agent\n// signed. Per the Sui GraphQL schema: \"transactions that interacted with the\n// given address … as a sender, sponsor, or recipient.\" The shared classifier\n// already labels each leg's direction ('in' / 'out'), so inbound txns render\n// correctly downstream.\nconst HISTORY_QUERY = `query History($address: SuiAddress!, $last: Int!) {\n  transactions(last: $last, filter: { affectedAddress: $address }) {\n    nodes {${TX_NODE_FRAGMENT}}\n  }\n}`;\n\nconst TX_BY_DIGEST_QUERY = `query TxByDigest($digest: String!) {\n  transaction(digest: $digest) {${TX_NODE_FRAGMENT}}\n}`;\n\n/** Minimal shape of a Sui GraphQL transaction node we consume. */\ninterface GqlTxNode {\n  digest?: string;\n  effects?: {\n    timestamp?: string;\n    // Live schema returns gas as numbers; `Number()` in the mapper tolerates both.\n    gasEffects?: { gasSummary?: { computationCost?: string | number; storageCost?: string | number; storageRebate?: string | number } };\n    balanceChanges?: { nodes?: Array<{ amount?: string; coinType?: { repr?: string }; owner?: { address?: string } }> };\n  };\n  kind?: {\n    __typename?: string;\n    commands?: {\n      nodes?: Array<{\n        __typename?: string;\n        function?: { name?: string; module?: { name?: string; package?: { address?: string } } };\n      }>;\n    };\n  };\n}\n\n/** GraphQL transport result with the optional `errors` array surfaced. */\ninterface GqlResult<T> {\n  data?: T;\n  errors?: Array<{ message?: string }>;\n}\n\nfunction assertNoGqlErrors(res: GqlResult<unknown>, what: string): void {\n  if (res.errors?.length) {\n    throw new Error(\n      `Sui GraphQL ${what} failed: ${res.errors.map((e) => e.message ?? 'unknown error').join('; ')}`,\n    );\n  }\n}\n\nexport async function queryHistory(\n  address: string,\n  limit = 20,\n): Promise<TransactionRecord[]> {\n  const gql = getSuiGraphQLClient();\n  const res = (await gql.query({ query: HISTORY_QUERY, variables: { address, last: limit } })) as GqlResult<{\n    transactions?: { nodes?: GqlTxNode[] };\n  }>;\n  assertNoGqlErrors(res, 'history query');\n  const nodes = res.data?.transactions?.nodes ?? [];\n  // GraphQL `last` returns ascending; the legacy JSON-RPC path returned\n  // descending (newest first) — reverse to preserve caller expectations.\n  return nodes.map((n) => recordFromGqlNode(n, address)).reverse();\n}\n\nexport async function queryTransaction(\n  digest: string,\n  senderAddress: string,\n): Promise<TransactionRecord | null> {\n  const gql = getSuiGraphQLClient();\n  const res = (await gql.query({ query: TX_BY_DIGEST_QUERY, variables: { digest } })) as GqlResult<{\n    transaction?: GqlTxNode | null;\n  }>;\n  assertNoGqlErrors(res, 'transaction query');\n  const node = res.data?.transaction;\n  if (!node) return null; // genuine not-found (distinct from a query error, which throws above)\n  return recordFromGqlNode(node, senderAddress);\n}\n\n/**\n * Map a Sui GraphQL transactionBlock node → the primitives the shared\n * classifier consumes, then build the record. Isolated so the legacy\n * JSON-RPC `parseTxRecord` (back-compat, below) and this GraphQL path\n * converge on one classification routine.\n */\nfunction recordFromGqlNode(node: GqlTxNode, address: string): TransactionRecord {\n  const gs = node.effects?.gasEffects?.gasSummary;\n  const gasCost = gs\n    ? (Number(gs.computationCost ?? 0) + Number(gs.storageCost ?? 0) - Number(gs.storageRebate ?? 0)) / 1e9\n    : undefined;\n\n  const balanceChanges: ClassifyBalanceChange[] = (node.effects?.balanceChanges?.nodes ?? [])\n    .filter((b) => b.coinType?.repr && b.amount != null)\n    .map((b) => ({ coinType: b.coinType!.repr!, amount: String(b.amount), owner: b.owner?.address ?? '' }));\n\n  const moveCallTargets: string[] = [];\n  const commandTypes: string[] = [];\n  if (node.kind?.__typename === 'ProgrammableTransaction') {\n    for (const cmd of node.kind.commands?.nodes ?? []) {\n      if (cmd.__typename === 'MoveCallCommand') {\n        commandTypes.push('MoveCall');\n        const fn = cmd.function;\n        const pkg = fn?.module?.package?.address;\n        const mod = fn?.module?.name;\n        const name = fn?.name;\n        if (pkg && mod && name) {\n          moveCallTargets.push(`${pkg}::${mod}::${name}`);\n        }\n      } else if (cmd.__typename === 'TransferObjectsCommand') {\n        commandTypes.push('TransferObjects');\n      }\n    }\n  }\n\n  // GraphQL effects.timestamp is an ISO datetime; the record wants ms.\n  const ts = node.effects?.timestamp ? Date.parse(node.effects.timestamp) : 0;\n\n  return buildRecord({\n    digest: node.digest ?? '',\n    moveCallTargets,\n    commandTypes,\n    balanceChanges,\n    timestampMs: Number.isFinite(ts) ? ts : 0,\n    gasCost,\n    address,\n  });\n}\n\n/** Shared record builder — classification + leg/transfer extraction. */\nfunction buildRecord(args: {\n  digest: string;\n  moveCallTargets: string[];\n  commandTypes: string[];\n  balanceChanges: ClassifyBalanceChange[];\n  timestampMs: number;\n  gasCost: number | undefined;\n  address: string;\n}): TransactionRecord {\n  const { digest, moveCallTargets, commandTypes, balanceChanges, timestampMs, gasCost, address } = args;\n  const legs = extractAllUserLegs(balanceChanges, address);\n  const { amount, asset, recipient, direction } = extractTransferDetails(balanceChanges, address);\n  const { action, label } = classifyTransaction(moveCallTargets, commandTypes, balanceChanges, address);\n  return { digest, action, label, legs, amount, asset, recipient, direction, timestamp: timestampMs, gasCost };\n}\n\n/**\n * Shape of a transaction block as returned by `suix_queryTransactionBlocks`\n * with `showEffects | showInput | showBalanceChanges` enabled. Exported so\n * downstream consumers (audric dashboard `/api/history`, `/api/activity`,\n * etc.) can type their RPC calls without redeclaring the structure.\n */\nexport interface SuiRpcTxBlock {\n  digest: string;\n  timestampMs?: string;\n  transaction?: unknown;\n  effects?: { gasUsed?: { computationCost: string; storageCost: string; storageRebate: string } };\n  balanceChanges?: ClassifyBalanceChange[];\n}\n\n/**\n * Convert a single Sui RPC transaction block to a {@link TransactionRecord}\n * using the canonical (shared) classifier and balance-change extractor.\n *\n * This is the single source of truth for transaction parsing across the\n * agent-tool path AND the dashboard-API path. Use it instead of writing\n * a bespoke parser per surface.\n *\n * @param tx      Raw RPC tx block (must include `effects`, `input`, `balanceChanges`).\n * @param address Wallet address whose perspective we're parsing from.\n */\nexport function parseSuiRpcTx(tx: SuiRpcTxBlock, address: string): TransactionRecord {\n  return parseTxRecord(tx, address);\n}\n\n/**\n * Extract the sender (signer) address from a raw RPC tx block.\n * Returns `null` if the block shape is unexpected.\n */\nexport function extractTxSender(txBlock: unknown): string | null {\n  try {\n    if (!txBlock || typeof txBlock !== 'object') return null;\n    const data = 'data' in txBlock ? (txBlock as Record<string, unknown>).data : undefined;\n    if (!data || typeof data !== 'object') return null;\n    return ((data as Record<string, unknown>).sender as string) ?? null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Extract MoveCall targets (`<pkg>::<module>::<function>`) and the\n * sequence of programmable-transaction command types (e.g. `MoveCall`,\n * `TransferObjects`) from a raw RPC tx block. Tolerates both the\n * legacy `inner.transactions` field and the newer `inner.commands`\n * field.\n */\nexport function extractTxCommands(txBlock: unknown): {\n  moveCallTargets: string[];\n  commandTypes: string[];\n} {\n  return extractCommands(txBlock);\n}\n\n// [back-compat] Legacy JSON-RPC-shape parser. The live history path is now\n// GraphQL (above); this stays for external consumers that already hold a\n// JSON-RPC tx block (e.g. via `parseSuiRpcTx`). Routes through `buildRecord`\n// so classification is identical to the GraphQL path.\nfunction parseTxRecord(tx: SuiRpcTxBlock, address: string): TransactionRecord {\n  const gasUsed = tx.effects?.gasUsed;\n  const gasCost = gasUsed\n    ? (Number(gasUsed.computationCost) +\n        Number(gasUsed.storageCost) -\n        Number(gasUsed.storageRebate)) /\n      1e9\n    : undefined;\n\n  const { moveCallTargets, commandTypes } = extractCommands(tx.transaction);\n  return buildRecord({\n    digest: tx.digest,\n    moveCallTargets,\n    commandTypes,\n    balanceChanges: tx.balanceChanges ?? [],\n    timestampMs: Number(tx.timestampMs ?? 0),\n    gasCost,\n    address,\n  });\n}\n\ninterface CommandInfo {\n  moveCallTargets: string[];\n  commandTypes: string[];\n}\n\nfunction extractCommands(txBlock: unknown): CommandInfo {\n  const result: CommandInfo = { moveCallTargets: [], commandTypes: [] };\n  try {\n    if (!txBlock || typeof txBlock !== 'object') return result;\n    const data = 'data' in txBlock ? (txBlock as Record<string, unknown>).data : undefined;\n    if (!data || typeof data !== 'object') return result;\n    const inner = 'transaction' in (data as Record<string, unknown>)\n      ? (data as Record<string, unknown>).transaction\n      : undefined;\n    if (!inner || typeof inner !== 'object') return result;\n    const commands = 'commands' in (inner as Record<string, unknown>)\n      ? (inner as Record<string, unknown>).commands\n      : 'transactions' in (inner as Record<string, unknown>)\n        ? (inner as Record<string, unknown>).transactions\n        : undefined;\n    if (!Array.isArray(commands)) return result;\n\n    for (const cmd of commands as Record<string, unknown>[]) {\n      if (cmd.MoveCall) {\n        const mc = cmd.MoveCall as { package: string; module: string; function: string };\n        result.moveCallTargets.push(`${mc.package}::${mc.module}::${mc.function}`);\n        result.commandTypes.push('MoveCall');\n      } else if (cmd.TransferObjects) {\n        result.commandTypes.push('TransferObjects');\n      }\n    }\n  } catch { /* best effort */ }\n  return result;\n}\n","import { MIST_PER_SUI, USDC_DECIMALS, SUPPORTED_ASSETS } from '../constants.js';\nimport type { SupportedAsset } from '../constants.js';\n\nexport function mistToSui(mist: bigint): number {\n  return Number(mist) / Number(MIST_PER_SUI);\n}\n\nexport function suiToMist(sui: number): bigint {\n  return BigInt(Math.round(sui * Number(MIST_PER_SUI)));\n}\n\nexport function usdcToRaw(amount: number): bigint {\n  return BigInt(Math.round(amount * 10 ** USDC_DECIMALS));\n}\n\nexport function rawToUsdc(raw: bigint): number {\n  return Number(raw) / 10 ** USDC_DECIMALS;\n}\n\nexport function stableToRaw(amount: number, decimals: number): bigint {\n  return BigInt(Math.round(amount * 10 ** decimals));\n}\n\nexport function rawToStable(raw: bigint, decimals: number): number {\n  return Number(raw) / 10 ** decimals;\n}\n\nexport function getDecimals(asset: SupportedAsset): number {\n  return SUPPORTED_ASSETS[asset].decimals;\n}\n\nexport function rawToDisplay(raw: bigint, decimals: number): number {\n  return Number(raw) / 10 ** decimals;\n}\n\nexport function displayToRaw(amount: number, decimals: number): bigint {\n  return BigInt(Math.round(amount * 10 ** decimals));\n}\n\nexport function formatUsd(amount: number): string {\n  return `$${amount.toFixed(2)}`;\n}\n\nexport function formatSui(amount: number): string {\n  if (amount < 0.001) return `${amount.toFixed(6)} SUI`;\n  return `${amount.toFixed(3)} SUI`;\n}\n\nexport function formatLargeNumber(n: number): string {\n  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n  if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n  return n.toFixed(2);\n}\n\nexport function formatAssetAmount(amount: number, asset: string): string {\n  if (asset === 'BTC') return amount.toFixed(8);\n  if (asset === 'GOLD') return amount.toFixed(6);\n  if (asset === 'ETH') return amount.toFixed(6);\n  return amount.toFixed(4);\n}\n\nconst ASSET_LOOKUP: Map<string, string> = new Map();\nfor (const [key, info] of Object.entries(SUPPORTED_ASSETS)) {\n  ASSET_LOOKUP.set(key.toUpperCase(), key);\n  if (info.displayName && info.displayName.toUpperCase() !== key.toUpperCase()) {\n    ASSET_LOOKUP.set(info.displayName.toUpperCase(), key);\n  }\n}\n\n/**\n * Case-insensitive lookup against SUPPORTED_ASSETS keys AND display names.\n * 'usde' → 'USDe', 'suiusde' → 'USDe', 'suiusdt' → 'USDT', 'usdsui' → 'USDsui'.\n * Returns the original input if not found so downstream validation can reject it.\n */\nexport function normalizeAsset(input: string): string {\n  return ASSET_LOOKUP.get(input.toUpperCase()) ?? input;\n}\n","/** Cross-platform (Node + browser) base64 helpers. */\n\nexport function toBase64(bytes: Uint8Array): string {\n  let binary = '';\n  for (const byte of bytes) binary += String.fromCharCode(byte);\n  return btoa(binary);\n}\n\nexport function fromBase64(b64: string): Uint8Array {\n  const binary = atob(b64);\n  const bytes = new Uint8Array(binary.length);\n  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n  return bytes;\n}\n","/**\n * Cetus Aggregator V3 SDK wrapper — the ONLY file that imports @cetusprotocol/aggregator-sdk.\n * Documented CLAUDE.md exception: multi-DEX routing cannot be feasibly replaced by thin tx builders.\n *\n * [B5 v2 / @t2000/sdk@1.1.0 / 2026-04-30]\n * Overlay fee config is now per-call instead of a module-level singleton. CLI / direct\n * SDK callers (`T2000.swap()`) DON'T pass `overlayFee` → fee-free swap. Audric's\n * prepare/route.ts ALWAYS passes `overlayFee = { rate: OVERLAY_FEE_RATE, receiver:\n * T2000_OVERLAY_FEE_WALLET }` → fee charged. Structural inclusion (Audric's code can't\n * forget to pass it because it IS the code), not a toggle that defaults to safe.\n *\n * Pre-1.1.0: a module-level `OVERLAY_FEE_RECEIVER` constant defaulted to a Move object\n * ID. USDC sent there became OwnedObjects keyed to the object and was inaccessible.\n * Fixed by making the receiver a regular wallet address (T2000_OVERLAY_FEE_WALLET) AND\n * by removing the singleton pattern that hid the misconfig.\n */\nimport { AggregatorClient, Env, type FindRouterParams, type RouterDataV3 } from '@cetusprotocol/aggregator-sdk';\nimport { Transaction, type TransactionObjectArgument } from '@mysten/sui/transactions';\nimport BN from 'bn.js';\nimport { resolveTokenType, getDecimalsForCoinType } from '../token-registry.js';\nimport type { SuiCoreClient } from '../utils/sui.js';\nimport type { SponsoredCoinMergeCache } from '../wallet/coinSelection.js';\nimport {\n  type PreflightResult,\n  PREFLIGHT_OK,\n  preflightFail,\n  checkPositiveAmount,\n} from '../preflight.js';\n\n/**\n * Synchronous, network-free preflight for `swap`. Validates the from/to token\n * args + amount sanity, and rejects an identity swap — the cheap checks the\n * host runs before routing. Returns a `PreflightResult`; never throws.\n * Route-finding, liquidity, slippage + decimals resolution stay in the async\n * path (`findSwapRoute`/`buildSwapTx`/`getSwapQuote`).\n *\n * v3 drops the swap *tool*, but `swap` survives as an SDK/CLI builder (§8) so\n * it gets the same builder-appropriate preflight as send/pay.\n */\nexport function preflightSwap(input: {\n  from: string;\n  to: string;\n  amount: number;\n}): PreflightResult {\n  if (typeof input.from !== 'string' || input.from.trim() === '') {\n    return preflightFail('INVALID_ASSET', 'A `from` token is required to swap');\n  }\n  if (typeof input.to !== 'string' || input.to.trim() === '') {\n    return preflightFail('INVALID_ASSET', 'A `to` token is required to swap');\n  }\n\n  // No upper ceiling — swaps route arbitrary tokens, and low-unit-value tokens\n  // legitimately trade in millions/billions of units.\n  const amountCheck = checkPositiveAmount(input.amount, 'Amount', Number.POSITIVE_INFINITY);\n  if (!amountCheck.valid) return amountCheck;\n\n  // Identity swap is a no-op the router can't route — catch it cheaply.\n  // Resolve symbol → coin type so \"USDC\" and the USDC coin type compare equal.\n  // Guard the null===null case (two unknown symbols both resolve to null) by\n  // also requiring the resolved value to be non-null, falling back to a raw\n  // string compare.\n  const resolvedFrom = resolveTokenType(input.from);\n  const resolvedTo = resolveTokenType(input.to);\n  const sameToken =\n    input.from.trim() === input.to.trim() ||\n    (resolvedFrom !== null && resolvedFrom === resolvedTo);\n  if (sameToken) {\n    return preflightFail('INVALID_ASSET', `Cannot swap ${input.from} to itself`);\n  }\n\n  return PREFLIGHT_OK;\n}\n\nexport interface OverlayFeeConfig {\n  /** Fee rate as a fraction (e.g. 0.001 = 0.1%). Pass 0 to disable. */\n  rate: number;\n  /** Wallet address that receives the overlay fee. */\n  receiver: string;\n}\n\nexport interface SwapRouteResult {\n  routerData: RouterDataV3;\n  amountIn: string;\n  amountOut: string;\n  byAmountIn: boolean;\n  priceImpact: number;\n  insufficientLiquidity: boolean;\n}\n\n// [SPEC 20.2 / D-1 (a)] Typed JSON-friendly representation of a Cetus\n// `RouterDataV3` for cross-process / cross-network transport. The native\n// shape contains `BN` instances (`bn.js`) and a `Map<string, string>`\n// (`packages`) that don't survive JSON.stringify / JSON.parse cleanly:\n// - `BN.toJSON()` returns the internal `{negative,words,length,red}` blob\n// - `Map.toJSON()` returns `{}`\n// Round-tripping via `serializeCetusRoute` + `deserializeCetusRoute`\n// converts BN ↔ decimal string and Map ↔ Record. Engine + audric both\n// reference fields by path (`route.routerData.paths[i].provider`) without\n// needing to know about the underlying BN/Map types.\nexport interface SerializedCetusRoutePath {\n  id: string;\n  direction: boolean;\n  provider: string;\n  from: string;\n  target: string;\n  feeRate: number;\n  amountIn: string;\n  amountOut: string;\n  version?: string;\n  publishedAt?: string;\n  extendedDetails?: Record<string, unknown>;\n}\n\nexport interface SerializedRouterDataV3 {\n  quoteID?: string;\n  /** RouterDataV3.amountIn (BN) → decimal string */\n  amountIn: string;\n  /** RouterDataV3.amountOut (BN) → decimal string */\n  amountOut: string;\n  byAmountIn: boolean;\n  paths: SerializedCetusRoutePath[];\n  insufficientLiquidity: boolean;\n  deviationRatio: number;\n  /** RouterDataV3.packages (Map) → Record */\n  packages?: Record<string, string>;\n  totalDeepFee?: number;\n  error?: { code: number; msg: string };\n  overlayFee?: number;\n}\n\nexport interface SerializedCetusRoute {\n  routerData: SerializedRouterDataV3;\n  amountIn: string;\n  amountOut: string;\n  byAmountIn: boolean;\n  priceImpact: number;\n  insufficientLiquidity: boolean;\n  /**\n   * Wall-clock timestamp (ms since epoch) at which the route was discovered.\n   * Used by audric's prepare-route for SPEC 20.2 D-3 TTL re-validation: if\n   * the route is older than the threshold AND price impact has shifted\n   * beyond tolerance, fall back to a fresh `findSwapRoute()` call.\n   */\n  discoveredAt: number;\n  /**\n   * Snapshot of the input/output coin types the route was discovered for.\n   * SPEC 20.2 D-2 (b) structural verification: prepare-route asserts\n   * input/output coins match before using the fast-path; mismatch falls\n   * back to fresh discovery (defense against client-side tampering and\n   * against legitimate token-type drift in the request).\n   */\n  fromCoinType: string;\n  toCoinType: string;\n}\n\nexport function serializeCetusRoute(\n  route: SwapRouteResult,\n  context: { fromCoinType: string; toCoinType: string },\n): SerializedCetusRoute {\n  return {\n    routerData: serializeRouterDataV3(route.routerData),\n    amountIn: route.amountIn,\n    amountOut: route.amountOut,\n    byAmountIn: route.byAmountIn,\n    priceImpact: route.priceImpact,\n    insufficientLiquidity: route.insufficientLiquidity,\n    discoveredAt: Date.now(),\n    fromCoinType: context.fromCoinType,\n    toCoinType: context.toCoinType,\n  };\n}\n\nexport function deserializeCetusRoute(serialized: SerializedCetusRoute): SwapRouteResult {\n  return {\n    routerData: deserializeRouterDataV3(serialized.routerData),\n    amountIn: serialized.amountIn,\n    amountOut: serialized.amountOut,\n    byAmountIn: serialized.byAmountIn,\n    priceImpact: serialized.priceImpact,\n    insufficientLiquidity: serialized.insufficientLiquidity,\n  };\n}\n\nfunction serializeRouterDataV3(rd: RouterDataV3): SerializedRouterDataV3 {\n  const out: SerializedRouterDataV3 = {\n    amountIn: rd.amountIn.toString(),\n    amountOut: rd.amountOut.toString(),\n    byAmountIn: rd.byAmountIn,\n    paths: rd.paths.map(serializeCetusRoutePath),\n    insufficientLiquidity: rd.insufficientLiquidity,\n    deviationRatio: rd.deviationRatio,\n  };\n  if (rd.quoteID !== undefined) out.quoteID = rd.quoteID;\n  if (rd.packages) {\n    const obj: Record<string, string> = {};\n    for (const [k, v] of rd.packages) obj[k] = v;\n    out.packages = obj;\n  }\n  if (rd.totalDeepFee !== undefined) out.totalDeepFee = rd.totalDeepFee;\n  if (rd.error) out.error = { code: rd.error.code, msg: rd.error.msg };\n  if (rd.overlayFee !== undefined) out.overlayFee = rd.overlayFee;\n  return out;\n}\n\nfunction deserializeRouterDataV3(s: SerializedRouterDataV3): RouterDataV3 {\n  const out: RouterDataV3 = {\n    amountIn: new BN(s.amountIn),\n    amountOut: new BN(s.amountOut),\n    byAmountIn: s.byAmountIn,\n    paths: s.paths.map(deserializeCetusRoutePath),\n    insufficientLiquidity: s.insufficientLiquidity,\n    deviationRatio: s.deviationRatio,\n  };\n  if (s.quoteID !== undefined) out.quoteID = s.quoteID;\n  if (s.packages) out.packages = new Map(Object.entries(s.packages));\n  if (s.totalDeepFee !== undefined) out.totalDeepFee = s.totalDeepFee;\n  if (s.error) out.error = { code: s.error.code, msg: s.error.msg };\n  if (s.overlayFee !== undefined) out.overlayFee = s.overlayFee;\n  return out;\n}\n\nfunction serializeCetusRoutePath(p: RouterDataV3['paths'][number]): SerializedCetusRoutePath {\n  const out: SerializedCetusRoutePath = {\n    id: p.id,\n    direction: p.direction,\n    provider: p.provider,\n    from: p.from,\n    target: p.target,\n    feeRate: p.feeRate,\n    amountIn: p.amountIn,\n    amountOut: p.amountOut,\n  };\n  if (p.version !== undefined) out.version = p.version;\n  if (p.publishedAt !== undefined) out.publishedAt = p.publishedAt;\n  if (p.extendedDetails) out.extendedDetails = { ...p.extendedDetails };\n  return out;\n}\n\nfunction deserializeCetusRoutePath(p: SerializedCetusRoutePath): RouterDataV3['paths'][number] {\n  const out: RouterDataV3['paths'][number] = {\n    id: p.id,\n    direction: p.direction,\n    provider: p.provider,\n    from: p.from,\n    target: p.target,\n    feeRate: p.feeRate,\n    amountIn: p.amountIn,\n    amountOut: p.amountOut,\n  };\n  if (p.version !== undefined) out.version = p.version;\n  if (p.publishedAt !== undefined) out.publishedAt = p.publishedAt;\n  if (p.extendedDetails) out.extendedDetails = { ...p.extendedDetails };\n  return out;\n}\n\n/**\n * SPEC 20.2 D-2 (b) structural verification helper. Returns true when the\n * serialized route matches the requested coin types (i.e. it's safe to use\n * as the prepare-route fast-path), false otherwise (tampered, or input\n * mismatch from a legitimate but stale pending action). Caller falls back\n * to a fresh `findSwapRoute()` call when verification fails.\n */\nexport function verifyCetusRouteCoinMatch(\n  serialized: SerializedCetusRoute,\n  expected: { fromCoinType: string; toCoinType: string },\n): boolean {\n  return serialized.fromCoinType === expected.fromCoinType && serialized.toCoinType === expected.toCoinType;\n}\n\n/**\n * SPEC 20.2 D-3 (b) TTL helper. Returns true when the serialized route is\n * fresh enough to use as the fast-path (< `maxAgeMs` old). Returns false\n * for stale routes — caller falls back to fresh `findSwapRoute()` to pick\n * up any pool-price drift since route discovery.\n *\n * Default 30s aligns with the existing quote-freshness contract surfaced\n * to users via `pending_action.quoteAge` (the PermissionCard \"QUOTE Ns OLD\"\n * badge starts warning the user past 30s).\n */\nexport function isCetusRouteFresh(serialized: SerializedCetusRoute, maxAgeMs: number = 30_000): boolean {\n  return Date.now() - serialized.discoveredAt < maxAgeMs;\n}\n\n/**\n * Default Audric swap overlay fee — 0.1%. Exported for consumers that want to use\n * the canonical Audric rate (the Audric prepare-route does this). Changing this\n * rate requires a coordinated SDK + audric release.\n */\nexport const OVERLAY_FEE_RATE = 0.001;\n\n/**\n * Cache `AggregatorClient` instances by `(signer + overlay rate + overlay receiver)`.\n * Per-call instantiation is cheap (the client is mostly config), but caching avoids\n * pointless re-allocation when the same caller swaps multiple times in a loop.\n */\nconst clientCache = new Map<string, AggregatorClient>();\n\nfunction getClient(walletAddress: string, overlayFee?: OverlayFeeConfig): AggregatorClient {\n  const rate = overlayFee?.rate ?? 0;\n  const receiver = overlayFee?.receiver ?? '';\n  const key = `${walletAddress}|${rate}|${receiver}`;\n\n  const cached = clientCache.get(key);\n  if (cached) return cached;\n\n  const client = new AggregatorClient({\n    signer: walletAddress,\n    env: Env.Mainnet,\n    ...(rate > 0 && receiver\n      ? { overlayFeeRate: rate, overlayFeeReceiver: receiver }\n      : {}),\n  });\n  clientCache.set(key, client);\n  return client;\n}\n\n/**\n * [Bug A defense-in-depth / 2026-05-10] Returns true when every path\n * provider in `route.routerData.paths` is present in the active\n * `providers` allow-list. Cetus's `getProvidersExcluding(...)` returns\n * an inclusion list (the complement of the exclusion), so when a caller\n * passes `providers`, every walked provider must be IN that list to be\n * compatible.\n *\n * When `providers` is undefined (non-sponsored caller, e.g. CLI) every\n * route is compatible — same semantics as `findSwapRoute` itself.\n *\n * Why per-path: a Cetus aggregator route can split across multiple DEXes\n * (e.g. 60% Cetus + 40% Bluefin). A single excluded provider in any path\n * triggers `tx.gas` usage in `routerSwap`. Reject the whole route if any\n * leg is excluded.\n */\nexport function isPrecomputedRouteCompatibleWithProviders(\n  route: SwapRouteResult,\n  providers: string[] | undefined,\n): boolean {\n  if (!providers || providers.length === 0) return true;\n  const allowed = new Set(providers);\n  for (const path of route.routerData.paths) {\n    if (!allowed.has(path.provider)) return false;\n  }\n  return true;\n}\n\n/**\n * Find the optimal swap route via Cetus Aggregator REST API.\n *\n * Pass `overlayFee` to charge an overlay fee on the output (Audric's pattern).\n * Omit it for a fee-free swap (CLI / direct SDK pattern).\n */\nexport async function findSwapRoute(params: {\n  walletAddress: string;\n  from: string;\n  to: string;\n  amount: bigint;\n  byAmountIn: boolean;\n  overlayFee?: OverlayFeeConfig;\n  /**\n   * Optional Cetus provider allow-list. When omitted, all 30+ DEXes\n   * are eligible. Sponsored flows (Enoki) MUST pass an exclusion list\n   * computed via `getProvidersExcluding([...])` from the Cetus SDK to\n   * remove Pyth-dependent providers (HAEDALPMM, METASTABLE, OBRIC,\n   * STEAMM_OMM, STEAMM_OMM_V2, SEVENK, HAEDALHMMV2) — those reference\n   * `tx.gas` for oracle fees, which Enoki rejects in sponsored txs.\n   * Non-sponsored callers (CLI, direct SDK) leave this undefined.\n   */\n  providers?: string[];\n}): Promise<SwapRouteResult | null> {\n  const client = getClient(params.walletAddress, params.overlayFee);\n\n  const findParams: FindRouterParams = {\n    from: params.from,\n    target: params.to,\n    amount: params.amount.toString(),\n    byAmountIn: params.byAmountIn,\n    ...(params.providers ? { providers: params.providers } : {}),\n  };\n\n  const routerData = await client.findRouters(findParams);\n  if (!routerData) return null;\n\n  if (routerData.insufficientLiquidity) {\n    return {\n      routerData,\n      amountIn: routerData.amountIn.toString(),\n      amountOut: routerData.amountOut.toString(),\n      byAmountIn: params.byAmountIn,\n      priceImpact: normalizePriceImpact(routerData.deviationRatio),\n      insufficientLiquidity: true,\n    };\n  }\n\n  if (routerData.error) {\n    const { T2000Error } = await import('../errors.js');\n    throw new T2000Error('SWAP_FAILED', `Cetus routing error: ${routerData.error.msg} (code ${routerData.error.code})`);\n  }\n\n  return {\n    routerData,\n    amountIn: routerData.amountIn.toString(),\n    amountOut: routerData.amountOut.toString(),\n    byAmountIn: params.byAmountIn,\n    priceImpact: normalizePriceImpact(routerData.deviationRatio),\n    insufficientLiquidity: false,\n  };\n}\n\n/**\n * Cetus' aggregator types `deviationRatio` as `number`, but in some routes\n * the router actually returns a string (\"0.001234\"). The SDK type lies, so we\n * always coerce to a finite number here (NaN/null/undefined → 0). Without\n * this every downstream consumer that calls `priceImpact.toFixed(...)` will\n * crash at runtime — including the Audric SwapQuoteCard, which takes the\n * whole chat UI down through its error boundary.\n */\nfunction normalizePriceImpact(value: unknown): number {\n  const n = typeof value === 'number' ? value : Number(value);\n  return Number.isFinite(n) ? n : 0;\n}\n\n/**\n * Build a swap PTB from a route result. The caller must provide an input coin\n * obtained by splitting/merging wallet coins.\n *\n * **Important:** Cetus's `routerSwap` reads the overlay-fee config from the\n * AggregatorClient instance. The `overlayFee` param here MUST match the one\n * passed to `findSwapRoute` for the same swap (otherwise you'll hit the cache\n * boundary and get a different client with different overlay config).\n */\nexport async function buildSwapTx(params: {\n  walletAddress: string;\n  route: SwapRouteResult;\n  tx: Transaction;\n  inputCoin: TransactionObjectArgument;\n  slippage: number;\n  overlayFee?: OverlayFeeConfig;\n}): Promise<TransactionObjectArgument> {\n  const client = getClient(params.walletAddress, params.overlayFee);\n  const clampedSlippage = Math.max(0.001, Math.min(params.slippage, 0.05));\n\n  const outputCoin = await client.routerSwap({\n    router: params.route.routerData,\n    inputCoin: params.inputCoin,\n    slippage: clampedSlippage,\n    txb: params.tx,\n  });\n\n  return outputCoin;\n}\n\n/**\n * Append a swap fragment to an existing PTB. SPEC 7 § \"Layer 1\" Cetus\n * appender. Two modes, dispatched by the presence of `input.inputCoin`:\n *\n * - **Wallet mode** (`inputCoin` omitted) — sources `from`-asset funds\n *   via `coinWithBalance({ type, balance })` (resolves coin objects +\n *   address balance at build time), runs the swap. Mirrors the audric\n *   host's `transactions/prepare/route.ts` swap branch (P2.2c will\n *   retire that branch in favor of this appender via `composeTx`).\n *\n * - **Chain mode** (`inputCoin` provided) — consumes the passed-in coin\n *   reference (typically produced by an upstream appender like\n *   `addWithdrawToTx`) directly, no wallet fetch / no merge / no\n *   split. This is the SPEC 7 multi-write enabler (\"withdraw → swap →\n *   save\" without intermediate wallet materialization).\n *\n * **SUI in wallet mode:** ALWAYS sources through `selectSuiCoin` (which\n * routes via `coinWithBalance({ type: SUI, useGasCoin: false })` under\n * sponsored flows, OR `tx.splitCoins(tx.gas, ...)` under self-funded\n * flows). The caller MUST set `sponsoredContext` correctly — otherwise\n * sponsored swaps with SUI source fail with `Cannot use GasCoin as a\n * transaction argument` (Enoki owns `tx.gas`, the PTB body referencing\n * it as an argument is invalid for sponsorship). 2.14.0 shipped without\n * this branch and broke audric/web-v2 SUI→USDC swaps; restored in 2.14.1\n * (S.260). For non-sponsored flows (CLI), `T2000.swap()` pre-builds the\n * inputCoin via `tx.splitCoins(tx.gas, [rawAmount])[0]` and uses chain\n * mode, sidestepping wallet-mode entirely — this branch is a defensive\n * safety net for future direct SDK users who pass SUI in wallet mode.\n *\n * **`swapAll` semantics (wallet mode):** if the requested raw amount\n * is >= the wallet's total `from` balance, the appender consumes the\n * entire merged primary coin (not a split), matching audric's host\n * route's `swapAll` clipping. The returned `effectiveAmountIn` reflects\n * the actual consumed amount in display units.\n *\n * **Slippage:** clamped to [0.001, 0.05] (0.1% – 5%). Defaults to 0.01.\n *\n * @returns\n * - `coin` — output coin reference, ready for downstream consumption\n *   (e.g. `addSaveToTx`) or wallet transfer (`tx.transferObjects`).\n * - `effectiveAmountIn` — display-units input amount the swap actually\n *   consumes (handles `swapAll` clipping in wallet mode; in chain mode\n *   echoes the requested `input.amount`).\n * - `expectedAmountOut` — display-units output amount per the route\n *   quote. Actual on-chain output may differ within slippage.\n * - `route` — raw `SwapRouteResult` for downstream telemetry / logging.\n */\nexport async function addSwapToTx(\n  tx: Transaction,\n  client: SuiCoreClient,\n  address: string,\n  input: {\n    from: string;\n    to: string;\n    amount: number;\n    slippage?: number;\n    byAmountIn?: boolean;\n    overlayFee?: OverlayFeeConfig;\n    inputCoin?: TransactionObjectArgument;\n    /**\n     * Optional Cetus provider allow-list. Forwarded to `findSwapRoute`.\n     * Sponsored flows (Enoki) MUST pass `getProvidersExcluding([...])`\n     * to remove Pyth-dependent providers — see `findSwapRoute`'s JSDoc\n     * for the exclusion list. Non-sponsored callers omit this.\n     */\n    providers?: string[];\n    /**\n     * [SPEC 20.2 D-3 (b)] Precomputed route from a prior `findSwapRoute()`\n     * call (typically captured by `swap_quote` and threaded through\n     * `pending_action.cetusRoute`). When present AND not stale (per\n     * `isCetusRouteFresh`) AND the input/output coins match, this skips\n     * the ~400-500ms `findSwapRoute()` discovery call. Stale routes are\n     * silently ignored (caller falls back to fresh discovery).\n     *\n     * Caller responsibility: pass the SAME `overlayFee` / `providers` /\n     * `byAmountIn` that produced the precomputed route. Mismatch will\n     * still produce a working swap but may use the wrong overlay-fee\n     * config (the route data already encodes the chosen DEX path).\n     */\n    precomputedRoute?: SwapRouteResult;\n    /**\n     * Whether this swap is being built inside a sponsored-tx flow (Enoki)\n     * vs self-funded (CLI / direct sign). Load-bearing for SUI-source\n     * swaps in wallet mode: under sponsored flows, `tx.gas` belongs to\n     * the sponsor and CANNOT be referenced as a transaction argument\n     * (Sui protocol rejects with `Cannot use GasCoin as a transaction\n     * argument`). When `true`, SUI source routes through `selectSuiCoin`\n     * with `useGasCoin: false` so the resolver sources from the user's\n     * SUI coin objects + address balance instead. Defaults to `false`\n     * (back-compat — pre-2.14.1 behavior). Audric/web-v2's compose path\n     * threads this through via `composeTx({ sponsoredContext: true })`.\n     */\n    sponsoredContext?: boolean;\n    /**\n     * Per-PTB merge cache for sponsored coin-object sourcing (any coin\n     * type — SUI in the dedicated branch, USDC/USDsui/etc. in the wallet\n     * branch). Provided by `composeTx`'s orchestration loop so multiple\n     * legs sourcing the same coin in one bundle share a single merged\n     * primary coin instead of each emitting its own `mergeCoins` (the\n     * second of which references already-consumed coins → Enoki dry-run\n     * `ArgumentWithoutValue`). Single swaps don't need it; omit. See\n     * `SponsoredCoinMergeCache` JSDoc.\n     */\n    coinMergeCache?: SponsoredCoinMergeCache;\n  },\n): Promise<{\n  coin: TransactionObjectArgument;\n  effectiveAmountIn: number;\n  expectedAmountOut: number;\n  route: SwapRouteResult;\n  /** True when `precomputedRoute` was used (no `findSwapRoute()` call). */\n  usedPrecomputedRoute: boolean;\n}> {\n  const { T2000Error } = await import('../errors.js');\n\n  const fromType = resolveTokenType(input.from);\n  const toType = resolveTokenType(input.to);\n  if (!fromType) throw new T2000Error('ASSET_NOT_SUPPORTED', `Unknown token: ${input.from}. Provide the symbol (USDC, SUI, ...) or full coin type.`);\n  if (!toType) throw new T2000Error('ASSET_NOT_SUPPORTED', `Unknown token: ${input.to}. Provide the symbol (USDC, SUI, ...) or full coin type.`);\n  if (fromType === toType) throw new T2000Error('SWAP_FAILED', 'Cannot swap a token to itself');\n  if (!Number.isFinite(input.amount) || input.amount <= 0) {\n    throw new T2000Error('INVALID_AMOUNT', 'Amount must be greater than zero');\n  }\n\n  const fromDecimals = getDecimalsForCoinType(fromType);\n  const toDecimals = getDecimalsForCoinType(toType);\n  const requestedRaw = BigInt(Math.floor(input.amount * 10 ** fromDecimals));\n\n  const slippage = Math.max(0.001, Math.min(input.slippage ?? 0.01, 0.05));\n  const byAmountIn = input.byAmountIn ?? true;\n\n  let inputCoin: TransactionObjectArgument;\n  let effectiveRaw: bigint;\n\n  if (input.inputCoin) {\n    inputCoin = input.inputCoin;\n    effectiveRaw = requestedRaw;\n  } else if (fromType === '0x2::sui::SUI') {\n    // SUI source needs special handling: under sponsored flows (Enoki),\n    // referencing `tx.gas` as a transaction argument is forbidden (sponsor\n    // owns the gas coin). `selectSuiCoin` does the right thing for both\n    // sponsored (uses `coinWithBalance({ useGasCoin: false })`) and\n    // self-funded (splits from `tx.gas` directly). The 2.14.0 release\n    // shipped without this branch and broke audric/web-v2 SUI→USDC swaps\n    // (\"Cannot use GasCoin as a transaction argument\" from Enoki); fixed\n    // in 2.14.1 (S.260).\n    const { selectSuiCoin } = await import('../wallet/coinSelection.js');\n    const result = await selectSuiCoin(\n      tx,\n      client,\n      address,\n      requestedRaw,\n      input.sponsoredContext ?? false,\n      input.coinMergeCache,\n    );\n    inputCoin = result.coin;\n    effectiveRaw = result.effectiveAmount;\n  } else {\n    // Non-SUI wallet-mode source — delegate to the canonical prelude.\n    // NON-sponsored: pre-flights against `getBalance().totalBalance` and\n    // returns a `coinWithBalance({ type, balance })` arg, whose `@mysten/sui`\n    // resolver batches all intents for a coin type into a single build-time\n    // merge — so same-asset multi-leg bundles dedup automatically.\n    //\n    // SPONSORED: routes through `selectCoinObjectsOnly` instead (coinWithBalance's\n    // address-balance `FundsWithdrawal` reservation is what Enoki can't\n    // deserialize, issue #93). That manual path has NO build-time batching, so\n    // two legs sourcing the same coin would each emit their own `mergeCoins`\n    // over the SAME objects → second merge references consumed coins → Enoki\n    // dry-run `ArgumentWithoutValue`. The `coinMergeCache` supplies the dedup:\n    // first leg merges once + caches the primary, later legs split from it.\n    const { selectAndSplitCoin } = await import('../wallet/coinSelection.js');\n    const result = await selectAndSplitCoin(tx, client, address, fromType, requestedRaw, {\n      sponsoredContext: input.sponsoredContext ?? false,\n      mergeCache: input.coinMergeCache,\n    });\n    inputCoin = result.coin;\n    effectiveRaw = result.effectiveAmount;\n  }\n\n  // [SPEC 20.2 D-3 (b)] Use the precomputed route when available + valid.\n  // Validity = same input/output coin types AND the requested raw amount\n  // matches what the route was discovered for (mismatch indicates the user\n  // edited the amount post-quote, in which case we must re-discover at the\n  // new amount because price impact is amount-dependent). The caller\n  // (`audric prepare-route`) owns the staleness check (`isCetusRouteFresh`)\n  // and only forwards `precomputedRoute` when fresh.\n  //\n  // [Bug A defense-in-depth / 2026-05-10] Even with the engine fix that\n  // makes `swap_quote` discover sponsor-safe routes, this layer also\n  // validates the precomputed route against the active providers\n  // allow-list. If the route walks any excluded provider (e.g. a stale\n  // `pending_action.cetusRoute` from before the engine fix shipped, or a\n  // provider list that drifted between quote and execute), fall back to\n  // fresh discovery. Better to eat 400ms than to let a Pyth-dependent\n  // route reach Enoki and revert with HTTP 400.\n  let route: SwapRouteResult | null;\n  let usedPrecomputedRoute = false;\n  if (\n    input.precomputedRoute &&\n    input.precomputedRoute.amountIn === effectiveRaw.toString() &&\n    input.precomputedRoute.byAmountIn === byAmountIn &&\n    isPrecomputedRouteCompatibleWithProviders(input.precomputedRoute, input.providers)\n  ) {\n    route = input.precomputedRoute;\n    usedPrecomputedRoute = true;\n  } else {\n    route = await findSwapRoute({\n      walletAddress: address,\n      from: fromType,\n      to: toType,\n      amount: effectiveRaw,\n      byAmountIn,\n      overlayFee: input.overlayFee,\n      providers: input.providers,\n    });\n  }\n\n  if (!route) {\n    throw new T2000Error('SWAP_NO_ROUTE', `No swap route found for ${input.from} → ${input.to}`);\n  }\n  if (route.insufficientLiquidity) {\n    throw new T2000Error('SWAP_NO_ROUTE', `Insufficient liquidity for ${input.from} → ${input.to}`);\n  }\n\n  const outputCoin = await buildSwapTx({\n    walletAddress: address,\n    route,\n    tx,\n    inputCoin,\n    slippage,\n    overlayFee: input.overlayFee,\n  });\n\n  return {\n    coin: outputCoin,\n    effectiveAmountIn: Number(effectiveRaw) / 10 ** fromDecimals,\n    expectedAmountOut: Number(route.amountOut) / 10 ** toDecimals,\n    route,\n    usedPrecomputedRoute,\n  };\n}\n\n// Re-export from the canonical token registry for backward-compat.\nexport { TOKEN_MAP, resolveTokenType } from '../token-registry.js';\n","import {\n  Transaction,\n  type TransactionObjectArgument,\n} from '@mysten/sui/transactions';\nimport {\n  GASLESS_MIN_STABLE_AMOUNT,\n  GASLESS_STABLE_TYPES,\n  SUPPORTED_ASSETS,\n  assertAllowedAsset,\n  type SendableAsset,\n} from '../constants.js';\nimport { T2000Error } from '../errors.js';\nimport { validateAddress, type SuiCoreClient } from '../utils/sui.js';\nimport { displayToRaw } from '../utils/format.js';\nimport {\n  type PreflightResult,\n  PREFLIGHT_OK,\n  preflightFail,\n  checkPositiveAmount,\n  checkSuiAddress,\n} from '../preflight.js';\n\n/**\n * Synchronous, network-free preflight for `send`. Validates asset membership,\n * amount sanity, the gasless stable floor, and recipient address shape — the\n * cheap checks the v3 host runs before the LLM round-trip / tap-to-confirm.\n * Returns a `PreflightResult`; never throws. `buildSendTx` calls this first,\n * then layers the network balance read on top.\n */\nexport function preflightSend(input: {\n  to: string;\n  amount: number;\n  asset: string;\n}): PreflightResult {\n  // Asset membership — reuse the canonical message (single source of truth).\n  try {\n    assertAllowedAsset('send', input.asset);\n  } catch (e) {\n    return preflightFail('INVALID_ASSET', (e as T2000Error).message);\n  }\n\n  const amountCheck = checkPositiveAmount(input.amount);\n  if (!amountCheck.valid) return amountCheck;\n\n  // Gasless protocol allowlist enforces a 0.01 minimum on the stables.\n  if (\n    (input.asset === 'USDC' || input.asset === 'USDsui') &&\n    input.amount < GASLESS_MIN_STABLE_AMOUNT\n  ) {\n    return preflightFail(\n      'INVALID_AMOUNT',\n      `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${input.asset}. Got ${input.amount}.`,\n    );\n  }\n\n  const addressCheck = checkSuiAddress(input.to);\n  if (!addressCheck.valid) return addressCheck;\n\n  return PREFLIGHT_OK;\n}\n\n/**\n * Build a PTB that sends `amount` of `asset` from `address` to `to`.\n *\n * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A]\n *\n * Asset constraint: `'USDC' | 'USDsui' | 'SUI'` only. Other assets throw\n * `INVALID_ASSET` via `assertAllowedAsset('send', asset)`. The constrained\n * set matches Sui mainnet's gasless allowlist (USDC + USDsui) plus SUI\n * for users who want a gas-native transfer.\n *\n * Build paths:\n * - **USDC / USDsui** — `0x2::balance::send_funds` Move call with a\n *   `tx.balance({ type, balance })` input. When built via `SuiGrpcClient`,\n *   the gRPC resolver auto-detects gasless eligibility and zeros gas.\n *   When built via `SuiJsonRpcClient`, the same PTB still executes but\n *   the caller pays normal gas. Minimum 0.01 (protocol allowlist floor).\n * - **SUI** — `tx.splitCoins(tx.gas, [amount]) → tx.transferObjects()`.\n *   Standard gas-native transfer. No minimum.\n *\n * Pre-flight balance check stays on JSON-RPC (`client.getBalance`) — it\n * sums coin objects + address balance so the legacy `getCoins` page miss\n * doesn't break for users whose stables landed via gasless deposits.\n *\n * `asset` is REQUIRED (no implicit USDC default — pre-v4 hid LLM intent\n * errors). Callers passing the wrong asset get an explicit error rather\n * than a silent currency substitution.\n */\nexport async function buildSendTx({\n  client,\n  address,\n  to,\n  amount,\n  asset,\n}: {\n  client: SuiCoreClient;\n  address: string;\n  to: string;\n  amount: number;\n  asset: SendableAsset;\n}): Promise<Transaction> {\n  // Layer 2 — cheap synchronous preflight (asset / amount / gasless floor /\n  // recipient shape). Rethrow the precise code+message verbatim.\n  const pf = preflightSend({ to, amount, asset });\n  if (!pf.valid) throw new T2000Error(pf.code, pf.error);\n\n  const recipient = validateAddress(to);\n  const assetInfo = SUPPORTED_ASSETS[asset];\n  if (!assetInfo) throw new T2000Error('ASSET_NOT_SUPPORTED', `Asset ${asset} is not supported`);\n\n  const rawAmount = displayToRaw(amount, assetInfo.decimals);\n  const tx = new Transaction();\n  tx.setSender(address);\n\n  // Balance pre-flight against `core.getBalance().balance.balance` (sums\n  // coins + address balance). The legacy `getCoins` page miss broke for\n  // users whose stables had drifted into address balance via @suimpp/mpp 0.7+.\n  const balanceResp = await client.core.getBalance({ owner: address, coinType: assetInfo.type });\n  const totalBalance = BigInt(balanceResp.balance.balance);\n  if (totalBalance < rawAmount) {\n    throw new T2000Error('INSUFFICIENT_BALANCE', `Insufficient ${asset} balance`, {\n      available: Number(totalBalance) / 10 ** assetInfo.decimals,\n      required: amount,\n    });\n  }\n\n  if (asset === 'SUI') {\n    // Standard gas-native transfer — split from the gas coin, transfer\n    // the resulting object. NOT gasless (SUI is not on the protocol\n    // allowlist for `balance::send_funds`).\n    const [sendCoin] = tx.splitCoins(tx.gas, [rawAmount]);\n    tx.transferObjects([sendCoin], recipient);\n    return tx;\n  }\n\n  // USDC / USDsui — gasless via `0x2::balance::send_funds`. The gRPC\n  // build resolver inspects this PTB shape at `tx.build()` time and,\n  // when it matches the protocol allowlist, sets gasPrice=0/gasBudget=0\n  // automatically. The Move signature is:\n  //   public fun send_funds<T>(balance: Balance<T>, recipient: address)\n  // `tx.balance({ type, balance })` produces a Balance<T> input sourced\n  // from the sender's address balance + coin objects (auto-merged).\n  const coinType = GASLESS_STABLE_TYPES[asset];\n  tx.moveCall({\n    target: '0x2::balance::send_funds',\n    typeArguments: [coinType],\n    arguments: [\n      tx.balance({ type: coinType, balance: rawAmount }),\n      tx.pure.address(recipient),\n    ],\n  });\n  return tx;\n}\n\n/**\n * Fragment-appender for the chain-mode send leg of SPEC 7 multi-write\n * Payment Intents. Consumes a coin reference produced by a previous\n * appender (e.g. `addWithdrawToTx`, `addSwapToTx`) and transfers it to\n * `recipient` within the same Payment Intent — no intermediate wallet\n * materialization.\n *\n * Codifies the hand-built send leg from\n * `scripts/smoke-spec7-withdraw-then-send.ts` (P2.1) into a typed\n * appender. SPEC 7 § \"Layer 1\" — P2.2b will register this in the\n * `WRITE_APPENDER_REGISTRY` under `send_transfer` for chain-mode\n * dispatch; the registry adapter will handle the wallet-fetch fallback\n * by delegating to `buildSendTx` when no upstream coin is available.\n *\n * For single-step send_transfer flows (no chained predecessor), use\n * `buildSendTx` directly — it builds a complete tx including the\n * wallet-coin selection / merge / split prelude.\n *\n * [v4.0 Phase A Day 2] Stays on the legacy `transferObjects` path\n * because chain-mode bundles are NEVER gasless — by definition they\n * combine multiple Move calls (`withdraw → send`, `swap → send`) which\n * fail the protocol allowlist check (only `balance::send_funds` and\n * a few related helpers are eligible). The bundled flow still works,\n * the user just pays gas (or has it sponsored by audric via Enoki).\n *\n * @returns void — the coin is consumed by `tx.transferObjects`. Callers\n *   that need the post-transfer \"effective amount\" should rely on the\n *   upstream appender's `effectiveAmount` (e.g. `addWithdrawToTx`'s\n *   return), not on this appender.\n */\nexport function addSendToTx(\n  tx: Transaction,\n  coin: TransactionObjectArgument,\n  recipient: string,\n): void {\n  const validRecipient = validateAddress(recipient);\n  tx.transferObjects([coin], validRecipient);\n}\n"]}