import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'; import { SuiGrpcClient } from '@mysten/sui/grpc'; import { SuiClientTypes } from '@mysten/sui/client'; import { TransactionObjectArgument, Transaction } from '@mysten/sui/transactions'; import { RouterDataV3 } from '@cetusprotocol/aggregator-sdk'; import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc'; /** * Abstract signing interface that decouples the SDK from any specific * key management strategy (Ed25519 keypair, zkLogin, multisig, …). */ interface TransactionSigner { getAddress(): string; signTransaction(txBytes: Uint8Array): Promise<{ signature: string; }>; /** * Sign an arbitrary personal message. Required by `@suimpp/mpp` 0.7+ for * grief-protection proofs (sender identity verification on settled payments). */ signPersonalMessage(messageBytes: Uint8Array): Promise<{ signature: string; bytes?: string; }>; } declare class KeypairSigner implements TransactionSigner { private readonly keypair; constructor(keypair: Ed25519Keypair); getAddress(): string; signTransaction(txBytes: Uint8Array): Promise<{ signature: string; }>; signPersonalMessage(messageBytes: Uint8Array): Promise<{ signature: string; bytes: string; }>; /** Access the underlying keypair for APIs that still require it directly. */ getKeypair(): Ed25519Keypair; } interface ZkLoginProof { proofPoints: { a: string[]; b: string[][]; c: string[]; }; issBase64Details: { indexMod4: number; value: string; }; headerBase64: string; addressSeed: string; } declare class ZkLoginSigner implements TransactionSigner { private readonly ephemeralKeypair; private readonly zkProof; private readonly userAddress; private readonly maxEpoch; constructor(ephemeralKeypair: Ed25519Keypair, zkProof: ZkLoginProof, userAddress: string, maxEpoch: number); getAddress(): string; signTransaction(txBytes: Uint8Array): Promise<{ signature: string; }>; signPersonalMessage(messageBytes: Uint8Array): Promise<{ signature: string; bytes: string; }>; isExpired(currentEpoch: number): boolean; } /** * [gRPC migration / S.438] Transport-agnostic client type for the migration. * * Both `SuiJsonRpcClient` and `SuiGrpcClient` extend `BaseClient` and expose * an identical `.core.*` API. During the migration, call sites are rewritten * from legacy methods (`getBalance`, `getObject`, `executeTransactionBlock`) * to the unified `client.core.*` API and typed against this union — so the * rewrite is behavior-preserving while still on JSON-RPC, and the final * transport flip (gRPC) is a one-line change in `getSuiClient()`. * * The query surface that has no gRPC `core` equivalent (`queryTransactionBlocks` * / `queryEvents`, Stage 0 finding A) uses `getSuiGraphQLClient()` instead. */ type SuiCoreClient = SuiJsonRpcClient | SuiGrpcClient; /** * [gRPC cutover] The canonical agent client is now gRPC. Mysten deactivates * the JSON-RPC fullnode interface on 2026-07-31, so reads (`.core.*`) and * execution route over gRPC — transaction *builds* already do * (`getSuiGrpcClient`). The whole SDK was first refactored onto the unified * `.core` API (behavior-preserving on JSON-RPC), then flipped here. The * `rpcUrl` arg / `T2000_RPC_URL` override now resolve a gRPC `baseUrl` * (`DEFAULT_RPC_URL` is the same host as `DEFAULT_GRPC_URL`). The historical * query surface stays on `getSuiGraphQLClient()`. */ declare function getSuiClient(rpcUrl?: string): SuiGrpcClient; /** * Cached `SuiGrpcClient` for gasless stablecoin transfer builds. * * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A] * * Why this exists: Sui mainnet's protocol-level gasless stablecoin transfers * (`0x2::balance::send_funds` on the USDC + USDsui allowlist) are detected * ONLY when the transaction is built through a `SuiGrpcClient`. The gRPC * client's build resolver inspects the PTB at `tx.build()` time and, if it * matches the gasless pattern, sets `gasPrice=0` + `gasBudget=0` automatically. * Building the SAME PTB through `SuiJsonRpcClient` produces the same bytes * but with non-zero gas — the tx still works, but the user pays SUI gas. * * Execution stays on JSON-RPC (`SuiJsonRpcClient.executeTransactionBlock`) * because (a) the rest of the SDK expects JSON-RPC and (b) Sui's docs * explicitly support a "build via gRPC, execute via JSON-RPC" hybrid: * https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers * * Override the endpoint with the `T2000_GRPC_URL` env var or the * `grpcUrl` arg. Cache is keyed by URL so multiple endpoints can * co-exist (e.g., the rare testnet smoke + production usage from the * same process). */ declare function getSuiGrpcClient(grpcUrl?: string): SuiGrpcClient; declare function validateAddress(address: string): string; declare function truncateAddress(address: string): string; /** * Normalize a Sui coin type to its canonical long-form 64-hex address. * `0x2::sui::SUI` → `0x0000…0002::sui::SUI`. Idempotent on already-long * forms. Returns the input unchanged if it doesn't look like a coin type * (`
::::`) so callers can pass arbitrary strings * without crashing. * * Why this exists: BlockVision's `/v2/sui/coin/price/list` endpoint * silently returns an empty `prices` map for short-form coin types * (notably `0x2::sui::SUI` — the native gas coin). Internal callers must * pass the long form, but external callers (LLM tool args, cached * coin-type strings, audit logs) commonly use the short form. Normalize * before the network call, denormalize back to the caller's input shape * after, and short/long become interchangeable. */ declare function normalizeCoinType(coinType: string): string; /** * Wallet-side coin selection helpers — single source of truth for the * "produce a `Coin` argument holding `amount` raw units of `coinType`, * owned by `address`" pattern. Used by every wallet-mode appender that * needs a coin input (save, send, swap, repay, stake, etc.). * * **2026-05-22 — address-balance migration.** Sui mainnet's address-balance * feature ships funds account-style instead of as discrete `Coin` objects. * After a payment via `0x2::balance::send_funds`, the leftover lands in the * sender's address balance with a synthetic "coin reservation" representing * the deposit. `client.getCoins()` correctly filters those reservations out * (they aren't real owned objects), so the old fetch+merge+split pattern * threw `INSUFFICIENT_BALANCE` for users whose stables had drifted into * address balance — even when `getBalance().totalBalance` showed plenty. * * The fix is structural: hand the work to `coinWithBalance({ type, balance })` * from `@mysten/sui/transactions`. Its build-time resolver inspects coins + * address balance together (`getBalance` + `listCoins`), then emits the * right shape — direct `redeem_funds` from address balance when AB ≥ * required, or merge-and-split across coins + AB withdrawal when not. Multi * intents per coin type get batched into a single merge in one PTB, so the * old per-PTB merge cache is no longer needed. * * Pre-flight uses `client.getBalance().totalBalance` (sums coins + AB) * instead of summing the paginated `getCoins` page. That's the OTHER half * of the migration — the legacy path could see `0` from `getCoins` and * mistakenly throw before `coinWithBalance` ever ran. */ interface CoinPage { ids: string[]; totalBalance: bigint; } /** * Sum every coin of `coinType` owned by `owner`, INCLUDING address balance. * Returns the IDs of any discrete coin objects that exist (callers * occasionally need this for non-`coinWithBalance` paths, e.g. SUIns name * registration which expects raw object IDs). * * Pre-2026-05-22 this function paginated `client.getCoins` and summed * the page balances. That misses address-balance funds (the SDK filters * them out of `getCoins` for back-compat). The new implementation calls * `getBalance` for the canonical total and `getCoins` for the optional * ID list — both round-trips, but they happen in parallel. */ declare function fetchAllCoins(client: SuiCoreClient, owner: string, coinType: string): Promise; interface SelectAndSplitResult { /** TransactionObjectArgument for a coin holding `effectiveAmount` raw units. */ coin: TransactionObjectArgument; /** Actual raw amount the returned coin holds. May be < requested if `swapAll` is true. */ effectiveAmount: bigint; /** True iff the request consumed the entire wallet balance (no split needed). */ swapAll: boolean; } /** * Wallet-mode coin selection prelude. Pre-flights against * `getBalance().totalBalance` (coins + address balance combined), then * returns a `coinWithBalance({ type, balance })` argument that the * `@mysten/sui` resolver fulfills at build time. * * Throws `T2000Error` (`INSUFFICIENT_BALANCE`) when: * - `amount` is bigger than the total balance AND the caller did NOT * opt into `swapAll: true` clipping. * - `amount === 'all'` AND total balance is zero. * * @param tx — PTB to register the `coinWithBalance` intent against. * @param client — Sui RPC client for the pre-flight `getBalance` lookup. * @param owner — wallet address whose coins to source from. * @param coinType — fully-qualified Sui coin type (e.g. `"0x...::usdc::USDC"`). * @param amount — raw amount to source (in MIST / smallest unit). Pass * `'all'` to consume the entire balance. * @param options.allowSwapAll — if true (default), `amount` >= totalBalance * auto-clips to total. If false, throws when the request would over-consume. * @param options.sponsoredContext — when true, source ONLY from discrete coin * objects (never the address balance). See the long note below — this exists * because Enoki's gas station can't yet deserialize a `TransactionData` that * contains the address-balance `FundsWithdrawal` reservation that * `coinWithBalance` emits. Self-funded callers leave this false: the fullnode * handles `FundsWithdrawal` fine, so the address-balance path is preferred * (it can reach funds that aren't held as coin objects). * * @returns * - `coin` — `TransactionObjectArgument` ready for downstream consumption. * - `effectiveAmount` — the raw amount the returned coin holds (handles * swapAll clipping). * - `swapAll` — true iff the entire balance was consumed. */ declare function selectAndSplitCoin(tx: Transaction, client: SuiCoreClient, owner: string, coinType: string, amount: bigint | 'all', options?: { allowSwapAll?: boolean; sponsoredContext?: boolean; mergeCache?: SponsoredCoinMergeCache; }): Promise; /** * Coin-object-only selection for sponsored (Enoki) transactions. Fetches the * owner's discrete `Coin` objects (NOT the address balance — `getCoins` * excludes it), merges them, and splits the requested amount. Never emits a * `FundsWithdrawal` reservation, so the resulting `TransactionData` stays on * the shape Enoki's gas station can serialize. * * Throws `ADDRESS_BALANCE_UNSPONSORABLE` when the coin objects don't cover the * request — which, for a user whose `getBalance().totalBalance` shows funds, * means those funds live in the address balance (e.g. received via a gasless * stablecoin transfer) and can't be moved through a sponsored transaction yet. */ /** * Per-PTB cache of merged sponsored coin-object primaries, keyed by coin * type. The FIRST `selectCoinObjectsOnly` call for a given coin type in a * PTB fetches the owner's discrete `Coin` objects, merges them into one * `primary`, and records it here alongside the remaining (unspent) balance. * EVERY subsequent leg sourcing the same coin type splits from that cached * `primary` instead of re-fetching + re-merging. * * Why this exists (S.xxx, 2026-06-02): a sponsored bundle with 2+ legs * sourcing the same coin (e.g. `SUI→WAL` + `SUI→DEEP`, or `swap USDC` + * `save USDC`) called `selectCoinObjectsOnly` once per leg. Each call * emitted its own `mergeCoins` over the SAME coin objects, so the second * leg's merge referenced coins the first leg already consumed → Enoki * dry-run failed with `CommandArgumentError { ArgumentWithoutValue }`. * * This is NOT SUI-specific. Under sponsorship, `selectAndSplitCoin` routes * EVERY asset through `selectCoinObjectsOnly` (the `coinWithBalance` * batching that would otherwise dedup these merges only runs for * non-sponsored CLI/direct flows — its address-balance `FundsWithdrawal` * reservation is what Enoki can't deserialize, issue #93). So the cache is * the dedup layer for ALL coin types in a sponsored multi-leg PTB, keyed * by coin type. SUI was simply the first asset observed failing in the * wild because it's the most common swap source. */ type SponsoredCoinMergeCache = Map; /** * SUI-specific coin selection. Branches on sponsorship context: * * - **Self-funded (`sponsoredContext: false`)** — splits from `tx.gas` * directly (the user's gas coin IS their SUI). More efficient — no * `getBalance` RTT. * * - **Sponsored (`sponsoredContext: true`)** — sources from the user's * discrete SUI coin objects (`selectCoinObjectsOnly`). This both (a) avoids * `tx.gas`, which belongs to the Enoki sponsor — NOT the user — under * sponsored flows (the original S.260 reason for `useGasCoin: false`), AND * (b) avoids `coinWithBalance`'s address-balance `FundsWithdrawal`, which * Enoki's gas station can't deserialize (issue #93). If the user's SUI is * address-balance-only, it raises `ADDRESS_BALANCE_UNSPONSORABLE`. */ declare function selectSuiCoin(tx: Transaction, client: SuiCoreClient, owner: string, amountMist: bigint, sponsoredContext: boolean, mergeCache?: SponsoredCoinMergeCache): Promise; type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'ADDRESS_BALANCE_UNSPONSORABLE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'WALLET_CORRUPT' | 'INVALID_KEY' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'INVALID_ASSET' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'CONTACT_NOT_FOUND' | 'INVALID_CONTACT_NAME' | 'SUINS_NOT_REGISTERED' | 'FACILITATOR_TIMEOUT' | 'SAFEGUARD_BLOCKED' | 'SWAP_NO_ROUTE' | 'SWAP_FAILED' | 'CHAIN_MODE_INVALID' | 'UNKNOWN'; interface T2000ErrorData { reason?: string; [key: string]: unknown; } declare class T2000Error extends Error { readonly code: T2000ErrorCode; readonly data?: T2000ErrorData; readonly retryable: boolean; constructor(code: T2000ErrorCode, message: string, data?: T2000ErrorData, retryable?: boolean); toJSON(): { retryable: boolean; data?: T2000ErrorData | undefined; error: T2000ErrorCode; message: string; }; } declare function mapWalletError(error: unknown): T2000Error; declare function mapMoveAbortCode(code: number): string; /** * The result of a synchronous preflight check. `valid: false` carries a * `T2000ErrorCode` + human message so the host can surface a precise reason * (and the builder can rethrow it as a `T2000Error` verbatim). */ type PreflightResult = { valid: true; } | { valid: false; code: T2000ErrorCode; error: string; }; /** * Fat-finger / overflow ceiling for an asset amount. NOT a spending policy * (that's the `@t2000/sdk/limits` module — USD-denominated, opt-in, stateful); * this is the "obviously wrong number" guard the safeguards rule prescribes * (`amount > 1_000_000` → unreasonable). */ declare const PREFLIGHT_MAX_AMOUNT = 1000000; declare const PREFLIGHT_OK: PreflightResult; declare function preflightFail(code: T2000ErrorCode, error: string): PreflightResult; /** * Pure positive-finite-amount sanity check (no network). Rejects NaN/Infinity * and non-positive values. The absurd-value ceiling defaults to * {@link PREFLIGHT_MAX_AMOUNT} but callers can raise it — pass * `max: Number.POSITIVE_INFINITY` for swaps of low-unit-value tokens * (memecoins legitimately trade in millions/billions of units), where a fixed * display-amount ceiling would be a false positive. */ declare function checkPositiveAmount(amount: number, label?: string, max?: number): PreflightResult; /** * Pure, synchronous Sui-address validity (no throw, no network) — the * non-throwing sibling of `validateAddress`. Use in preflight; use * `validateAddress` when you need the normalized form. */ declare function checkSuiAddress(address: string, label?: string): PreflightResult; /** * Unified token registry — single source of truth for coin types, decimals, * and symbols. ZERO heavy dependencies; safe to import anywhere (server, * browser, Edge). * * Used for swap-by-symbol resolution, amount formatting (decimals), and * history/balance display. USDC is the settlement stable (send / receive / * x402 pay); everything else is holdable / swappable. There is no curated * "tier" gate — swaps accept any coin type (Cetus routes); the registry just * makes the common tokens ergonomic to reference by symbol. * * To add a token: add ONE entry to COIN_REGISTRY below — everything derives * from it. */ interface CoinMeta { type: string; decimals: number; symbol: string; } /** * Canonical coin registry. * Key = user-friendly name (used in swap, CLI, prompts). */ declare const COIN_REGISTRY: Record; /** * Returns the registry metadata for a coin type, or `undefined` if the coin * is not in the registry. Used for canonical-symbol + decimal resolution. */ declare function getCoinMeta(coinType: string): CoinMeta | undefined; /** * Returns true if the coin type appears in COIN_REGISTRY. Useful as a * canonical-symbol gate — e.g. so the registry's mixed-case `USDsui` wins * over a vendor feed's uppercase `USDSUI`. */ declare function isInRegistry(coinType: string): boolean; /** * Get decimals for any coin type. Checks full type match, then suffix match, * then defaults to 9. Works for any token, registered or not. */ declare function getDecimalsForCoinType(coinType: string): number; /** * Resolve a full coin type to a user-friendly symbol. * Returns the last `::` segment if not in the registry. */ declare function resolveSymbol(coinType: string): string; /** * Name → type map for swap resolution. Derived from COIN_REGISTRY. * Contains BOTH original-case and UPPERCASE keys for case-insensitive lookup. */ declare const TOKEN_MAP: Record; /** * Resolve a user-friendly token name to its full coin type. * Returns the input unchanged if already a full coin type (contains "::"). * Case-insensitive: 'usde', 'USDe', 'USDE' all resolve correctly. */ declare function resolveTokenType(nameOrType: string): string | null; /** Common type constants for direct import. */ declare const SUI_TYPE: string; declare const USDC_TYPE: string; declare const USDT_TYPE: string; declare const USDSUI_TYPE: string; declare const USDE_TYPE: string; declare const ETH_TYPE: string; declare const WBTC_TYPE: string; declare const WAL_TYPE: string; declare const NAVX_TYPE: string; declare const IKA_TYPE: string; declare const LOFI_TYPE: string; declare const MANIFEST_TYPE: string; /** * Cetus Aggregator V3 SDK wrapper — the ONLY file that imports @cetusprotocol/aggregator-sdk. * Documented CLAUDE.md exception: multi-DEX routing cannot be feasibly replaced by thin tx builders. * * [B5 v2 / @t2000/sdk@1.1.0 / 2026-04-30] * Overlay fee config is now per-call instead of a module-level singleton. CLI / direct * SDK callers (`T2000.swap()`) DON'T pass `overlayFee` → fee-free swap. Audric's * prepare/route.ts ALWAYS passes `overlayFee = { rate: OVERLAY_FEE_RATE, receiver: * T2000_OVERLAY_FEE_WALLET }` → fee charged. Structural inclusion (Audric's code can't * forget to pass it because it IS the code), not a toggle that defaults to safe. * * Pre-1.1.0: a module-level `OVERLAY_FEE_RECEIVER` constant defaulted to a Move object * ID. USDC sent there became OwnedObjects keyed to the object and was inaccessible. * Fixed by making the receiver a regular wallet address (T2000_OVERLAY_FEE_WALLET) AND * by removing the singleton pattern that hid the misconfig. */ /** * Synchronous, network-free preflight for `swap`. Validates the from/to token * args + amount sanity, and rejects an identity swap — the cheap checks the * host runs before routing. Returns a `PreflightResult`; never throws. * Route-finding, liquidity, slippage + decimals resolution stay in the async * path (`findSwapRoute`/`buildSwapTx`/`getSwapQuote`). * * v3 drops the swap *tool*, but `swap` survives as an SDK/CLI builder (§8) so * it gets the same builder-appropriate preflight as send/pay. */ declare function preflightSwap(input: { from: string; to: string; amount: number; }): PreflightResult; interface OverlayFeeConfig { /** Fee rate as a fraction (e.g. 0.001 = 0.1%). Pass 0 to disable. */ rate: number; /** Wallet address that receives the overlay fee. */ receiver: string; } interface SwapRouteResult { routerData: RouterDataV3; amountIn: string; amountOut: string; byAmountIn: boolean; priceImpact: number; insufficientLiquidity: boolean; } interface SerializedCetusRoutePath { id: string; direction: boolean; provider: string; from: string; target: string; feeRate: number; amountIn: string; amountOut: string; version?: string; publishedAt?: string; extendedDetails?: Record; } interface SerializedRouterDataV3 { quoteID?: string; /** RouterDataV3.amountIn (BN) → decimal string */ amountIn: string; /** RouterDataV3.amountOut (BN) → decimal string */ amountOut: string; byAmountIn: boolean; paths: SerializedCetusRoutePath[]; insufficientLiquidity: boolean; deviationRatio: number; /** RouterDataV3.packages (Map) → Record */ packages?: Record; totalDeepFee?: number; error?: { code: number; msg: string; }; overlayFee?: number; } interface SerializedCetusRoute { routerData: SerializedRouterDataV3; amountIn: string; amountOut: string; byAmountIn: boolean; priceImpact: number; insufficientLiquidity: boolean; /** * Wall-clock timestamp (ms since epoch) at which the route was discovered. * Used by audric's prepare-route for SPEC 20.2 D-3 TTL re-validation: if * the route is older than the threshold AND price impact has shifted * beyond tolerance, fall back to a fresh `findSwapRoute()` call. */ discoveredAt: number; /** * Snapshot of the input/output coin types the route was discovered for. * SPEC 20.2 D-2 (b) structural verification: prepare-route asserts * input/output coins match before using the fast-path; mismatch falls * back to fresh discovery (defense against client-side tampering and * against legitimate token-type drift in the request). */ fromCoinType: string; toCoinType: string; } declare function serializeCetusRoute(route: SwapRouteResult, context: { fromCoinType: string; toCoinType: string; }): SerializedCetusRoute; declare function deserializeCetusRoute(serialized: SerializedCetusRoute): SwapRouteResult; /** * SPEC 20.2 D-2 (b) structural verification helper. Returns true when the * serialized route matches the requested coin types (i.e. it's safe to use * as the prepare-route fast-path), false otherwise (tampered, or input * mismatch from a legitimate but stale pending action). Caller falls back * to a fresh `findSwapRoute()` call when verification fails. */ declare function verifyCetusRouteCoinMatch(serialized: SerializedCetusRoute, expected: { fromCoinType: string; toCoinType: string; }): boolean; /** * SPEC 20.2 D-3 (b) TTL helper. Returns true when the serialized route is * fresh enough to use as the fast-path (< `maxAgeMs` old). Returns false * for stale routes — caller falls back to fresh `findSwapRoute()` to pick * up any pool-price drift since route discovery. * * Default 30s aligns with the existing quote-freshness contract surfaced * to users via `pending_action.quoteAge` (the PermissionCard "QUOTE Ns OLD" * badge starts warning the user past 30s). */ declare function isCetusRouteFresh(serialized: SerializedCetusRoute, maxAgeMs?: number): boolean; /** * Default Audric swap overlay fee — 0.1%. Exported for consumers that want to use * the canonical Audric rate (the Audric prepare-route does this). Changing this * rate requires a coordinated SDK + audric release. */ declare const OVERLAY_FEE_RATE = 0.001; /** * Find the optimal swap route via Cetus Aggregator REST API. * * Pass `overlayFee` to charge an overlay fee on the output (Audric's pattern). * Omit it for a fee-free swap (CLI / direct SDK pattern). */ declare function findSwapRoute(params: { walletAddress: string; from: string; to: string; amount: bigint; byAmountIn: boolean; overlayFee?: OverlayFeeConfig; /** * Optional Cetus provider allow-list. When omitted, all 30+ DEXes * are eligible. Sponsored flows (Enoki) MUST pass an exclusion list * computed via `getProvidersExcluding([...])` from the Cetus SDK to * remove Pyth-dependent providers (HAEDALPMM, METASTABLE, OBRIC, * STEAMM_OMM, STEAMM_OMM_V2, SEVENK, HAEDALHMMV2) — those reference * `tx.gas` for oracle fees, which Enoki rejects in sponsored txs. * Non-sponsored callers (CLI, direct SDK) leave this undefined. */ providers?: string[]; }): Promise; /** * Build a swap PTB from a route result. The caller must provide an input coin * obtained by splitting/merging wallet coins. * * **Important:** Cetus's `routerSwap` reads the overlay-fee config from the * AggregatorClient instance. The `overlayFee` param here MUST match the one * passed to `findSwapRoute` for the same swap (otherwise you'll hit the cache * boundary and get a different client with different overlay config). */ declare function buildSwapTx(params: { walletAddress: string; route: SwapRouteResult; tx: Transaction; inputCoin: TransactionObjectArgument; slippage: number; overlayFee?: OverlayFeeConfig; }): Promise; /** * Append a swap fragment to an existing PTB. SPEC 7 § "Layer 1" Cetus * appender. Two modes, dispatched by the presence of `input.inputCoin`: * * - **Wallet mode** (`inputCoin` omitted) — sources `from`-asset funds * via `coinWithBalance({ type, balance })` (resolves coin objects + * address balance at build time), runs the swap. Mirrors the audric * host's `transactions/prepare/route.ts` swap branch (P2.2c will * retire that branch in favor of this appender via `composeTx`). * * - **Chain mode** (`inputCoin` provided) — consumes the passed-in coin * reference (typically produced by an upstream appender like * `addWithdrawToTx`) directly, no wallet fetch / no merge / no * split. This is the SPEC 7 multi-write enabler ("withdraw → swap → * save" without intermediate wallet materialization). * * **SUI in wallet mode:** ALWAYS sources through `selectSuiCoin` (which * routes via `coinWithBalance({ type: SUI, useGasCoin: false })` under * sponsored flows, OR `tx.splitCoins(tx.gas, ...)` under self-funded * flows). The caller MUST set `sponsoredContext` correctly — otherwise * sponsored swaps with SUI source fail with `Cannot use GasCoin as a * transaction argument` (Enoki owns `tx.gas`, the PTB body referencing * it as an argument is invalid for sponsorship). 2.14.0 shipped without * this branch and broke audric/web-v2 SUI→USDC swaps; restored in 2.14.1 * (S.260). For non-sponsored flows (CLI), `T2000.swap()` pre-builds the * inputCoin via `tx.splitCoins(tx.gas, [rawAmount])[0]` and uses chain * mode, sidestepping wallet-mode entirely — this branch is a defensive * safety net for future direct SDK users who pass SUI in wallet mode. * * **`swapAll` semantics (wallet mode):** if the requested raw amount * is >= the wallet's total `from` balance, the appender consumes the * entire merged primary coin (not a split), matching audric's host * route's `swapAll` clipping. The returned `effectiveAmountIn` reflects * the actual consumed amount in display units. * * **Slippage:** clamped to [0.001, 0.05] (0.1% – 5%). Defaults to 0.01. * * @returns * - `coin` — output coin reference, ready for downstream consumption * (e.g. `addSaveToTx`) or wallet transfer (`tx.transferObjects`). * - `effectiveAmountIn` — display-units input amount the swap actually * consumes (handles `swapAll` clipping in wallet mode; in chain mode * echoes the requested `input.amount`). * - `expectedAmountOut` — display-units output amount per the route * quote. Actual on-chain output may differ within slippage. * - `route` — raw `SwapRouteResult` for downstream telemetry / logging. */ declare function addSwapToTx(tx: Transaction, client: SuiCoreClient, address: string, input: { from: string; to: string; amount: number; slippage?: number; byAmountIn?: boolean; overlayFee?: OverlayFeeConfig; inputCoin?: TransactionObjectArgument; /** * Optional Cetus provider allow-list. Forwarded to `findSwapRoute`. * Sponsored flows (Enoki) MUST pass `getProvidersExcluding([...])` * to remove Pyth-dependent providers — see `findSwapRoute`'s JSDoc * for the exclusion list. Non-sponsored callers omit this. */ providers?: string[]; /** * [SPEC 20.2 D-3 (b)] Precomputed route from a prior `findSwapRoute()` * call (typically captured by `swap_quote` and threaded through * `pending_action.cetusRoute`). When present AND not stale (per * `isCetusRouteFresh`) AND the input/output coins match, this skips * the ~400-500ms `findSwapRoute()` discovery call. Stale routes are * silently ignored (caller falls back to fresh discovery). * * Caller responsibility: pass the SAME `overlayFee` / `providers` / * `byAmountIn` that produced the precomputed route. Mismatch will * still produce a working swap but may use the wrong overlay-fee * config (the route data already encodes the chosen DEX path). */ precomputedRoute?: SwapRouteResult; /** * Whether this swap is being built inside a sponsored-tx flow (Enoki) * vs self-funded (CLI / direct sign). Load-bearing for SUI-source * swaps in wallet mode: under sponsored flows, `tx.gas` belongs to * the sponsor and CANNOT be referenced as a transaction argument * (Sui protocol rejects with `Cannot use GasCoin as a transaction * argument`). When `true`, SUI source routes through `selectSuiCoin` * with `useGasCoin: false` so the resolver sources from the user's * SUI coin objects + address balance instead. Defaults to `false` * (back-compat — pre-2.14.1 behavior). Audric/web-v2's compose path * threads this through via `composeTx({ sponsoredContext: true })`. */ sponsoredContext?: boolean; /** * Per-PTB merge cache for sponsored coin-object sourcing (any coin * type — SUI in the dedicated branch, USDC/USDsui/etc. in the wallet * branch). Provided by `composeTx`'s orchestration loop so multiple * legs sourcing the same coin in one bundle share a single merged * primary coin instead of each emitting its own `mergeCoins` (the * second of which references already-consumed coins → Enoki dry-run * `ArgumentWithoutValue`). Single swaps don't need it; omit. See * `SponsoredCoinMergeCache` JSDoc. */ coinMergeCache?: SponsoredCoinMergeCache; }): Promise<{ coin: TransactionObjectArgument; effectiveAmountIn: number; expectedAmountOut: number; route: SwapRouteResult; /** True when `precomputedRoute` was used (no `findSwapRoute()` call). */ usedPrecomputedRoute: boolean; }>; interface T2000Options { keyPath?: string; /** PIN to decrypt the key file. Accepts any string (4+ chars). */ pin?: string; /** @deprecated Use `pin` instead. */ passphrase?: string; network?: 'mainnet' | 'testnet'; rpcUrl?: string; } interface SuiHolding { /** SUI balance in whole SUI (not MIST). */ amount: number; /** USD value of the SUI holding. */ usdValue: number; } /** * A non-stable, non-SUI coin held in the wallet (e.g. a token swapped into). * `usdValue` is `null` — the SDK only prices stables ($1) + SUI (Cetus oracle); * arbitrary tokens have no price feed (no BlockVision), so we surface the * amount honestly rather than guessing a USD value. */ interface TokenHolding { coinType: string; symbol: string; amount: number; usdValue: number | null; } interface BalanceResponse { /** Spendable stablecoins keyed by symbol (USDC, USDsui) — gasless to send/pay. */ stables: Record; /** Sum of spendable stables in USD. Used for send/pay pre-checks. */ available: number; /** SUI holding — gas for swaps + any non-gasless op. Not a "reserve". */ sui: SuiHolding; /** Other held coins (anything not a stable or SUI) — amount-only, `usdValue: null`. */ tokens: TokenHolding[]; /** * Total wallet value in USD — **priced holdings only** (stables + SUI). Tokens * with `usdValue: null` are listed in `tokens` but NOT summed here, so the * total never overstates by guessing a price. `tokens.length > 0` signals * there's more in the wallet than the USD total reflects. */ totalUsd: number; } interface SendResult { success: boolean; tx: string; amount: number; to: string; /** * [S.279] Set when the recipient was resolved via SuiNS (e.g. `alex.sui`). * CLI receipts render "Sent to alex.sui (0xabc...)" when present. */ suinsName?: string; gasCost: number; gasCostUnit: string; balance: BalanceResponse; } interface DepositInfo { address: string; network: string; supportedAssets: string[]; instructions: string; } interface PaymentRequest { address: string; network: string; amount: number | null; currency: string; memo: string | null; label: string | null; /** Unique payment identifier (UUID) for Payment Kit registry */ nonce: string; /** Payment Kit URI (sui:pay?...) for QR codes and wallet deep links */ qrUri: string; /** Human-readable summary */ displayText: string; } /** * One non-zero user balance change for a transaction. Sui collapses * balance changes by coin type, so a 3-step bundle that touches USDC * three times surfaces as ONE leg of net USDC delta — not three. * * [Activity rebuild / 2026-05-10] Added so consumers can render swap * + bundle txs accurately instead of picking a single "primary leg" * (which made `Swapped 987.60 MANIFEST` look like +$987 of value when * the user actually paid 1 USDC for it). */ interface TransactionLeg { /** Full Sui coin type string (e.g. `0x...usdc::USDC`). */ coinType: string; /** Display symbol (USDC, SUI, GOLD, MANIFEST, …) from the token registry. */ asset: string; /** On-chain decimals for this coin (used to format `amount`). */ decimals: number; /** Token quantity as a positive number (e.g. 987.60). */ amount: number; /** Signed raw bigint as a string (preserves sign + precision). */ rawAmount: string; /** `'out'` if the user spent this coin, `'in'` if they received it. */ direction: 'in' | 'out'; } interface TransactionRecord { digest: string; /** Coarse bucket — `'send' | 'lending' | 'swap' | 'transaction'`. STABLE. */ action: string; /** * Finer-grained display label derived from the Move-call function * name (e.g. `'deposit'`, `'withdraw'`, `'payment_link'`, * `'on-chain'`). Optional — frontends should fall back to `action` * when missing. Never used by ACI filters. */ label?: string; /** * All non-zero user balance legs for this transaction. Single-write * txs have `legs.length === 1`; swaps have `2` (one `out`, one * `in`); bundles have `> 2`. Order is RPC order — not sorted by * size or USD value (audric's activity route prices + sorts). * * @since SDK v1.27.2 — was missing from earlier shapes; older * consumers can keep using `amount` / `asset` / `direction` (which * still resolve to the largest absolute leg). */ legs: TransactionLeg[]; /** * Largest-absolute-leg amount, kept for back-compat with consumers * that pre-date `legs[]`. New code should iterate `legs` instead. */ amount?: number; /** @see {@link amount} — back-compat alias for `legs[primary].asset`. */ asset?: string; recipient?: string; /** * Direction of the user's principal (non-gas) balance movement on * this tx — `'out'` if they spent, `'in'` if they received. * Computed from on-chain balance changes (NOT from `label`), so the * card can render the correct sign even for opaque actions like * `swap`/`router`. Undefined when no user balance change is * detectable (e.g. pure read-only or admin txs). * * @see {@link amount} — back-compat alias for `legs[primary].direction`. */ direction?: 'in' | 'out'; timestamp: number; gasCost?: number; } interface SwapResult { success: boolean; tx: string; fromToken: string; toToken: string; fromAmount: number; toAmount: number; priceImpact: number; route: string; gasCost: number; } interface SwapQuoteResult { fromToken: string; toToken: string; fromAmount: number; toAmount: number; priceImpact: number; route: string; /** * [SPEC 20.2 / D-1 (a)] Structured Cetus route captured at quote time. * Threaded through `pending_action.cetusRoute` so the prepare-route can * skip the ~400-500ms `findSwapRoute()` re-discovery, and so the * post-write resume system prompt can ground LLM narration against the * canonical route (closing S19-F2). Optional for backward compat with * pre-SPEC-20.2 callers (CLI, server-only direct calls). */ serializedRoute?: SerializedCetusRoute; } interface PayOptions { url: string; method?: string; body?: string; headers?: Record; maxPrice?: number; /** Bypass the spending-limit gate for this call (caller owns consent). */ force?: boolean; } interface PayResult { status: number; body: unknown; paid: boolean; /** * Which payment dialect settled the call. `'x402'` = the sign-then-settle * x402 `sui-exact` scheme (client signs, gateway settles); `'legacy'` = the * pre-x402 MPP digest dialect (client broadcasts, retries with the digest). * Undefined when nothing was paid (free/cached endpoint). See * SUIMPP_X402_SCHEME.md. */ dialect?: 'x402' | 'legacy'; cost?: number; /** * SUI gas cost actually paid on chain. Zero for gasless payments — * which means an MPP payment hit the protocol's gasless allowlist * (USDC / USDsui / USDY / FdUSD / AUSD / BUCK / USDB / SUI_USDE) and * was accepted with `gasPrice=0, gasBudget=0, gasPayment=[]`. See * https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers */ gasCostSui?: number; receipt?: { reference: string; timestamp: string; }; } /** * Synchronous, network-free preflight for `pay` (x402 Service call). Validates * the target URL shape and the `maxPrice` ceiling when present — the cheap * checks the v3 host runs before dispatching the paid tool / showing the * tap-to-confirm card. Returns a `PreflightResult`; never throws. The probe + * 402 handshake + balance migration stay in `payWithMpp` (network). */ declare function preflightPay(input: { url: string; maxPrice?: number; }): PreflightResult; declare function payWithMpp(args: { signer: TransactionSigner; client: SuiGrpcClient; options: PayOptions; }): Promise; type SuiTransactionEffects = SuiClientTypes.TransactionEffects; type BuildClient = NonNullable[0]>['client']; declare function executeTx(client: SuiCoreClient, signer: TransactionSigner, buildTx: () => Promise | Transaction, options?: { buildClient?: BuildClient; }): Promise<{ digest: string; gasCostSui: number; effects: SuiTransactionEffects | undefined; }>; declare const MIST_PER_SUI = 1000000000n; declare const SUI_DECIMALS = 9; declare const USDC_DECIMALS = 6; declare const CLOCK_ID = "0x6"; declare const SUPPORTED_ASSETS: { readonly USDC: { readonly type: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"; readonly decimals: 6; readonly symbol: "USDC"; readonly displayName: "USDC"; }; readonly USDT: { readonly type: "0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT"; readonly decimals: 6; readonly symbol: "USDT"; readonly displayName: "suiUSDT"; }; readonly USDe: { readonly type: "0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE"; readonly decimals: 6; readonly symbol: "USDe"; readonly displayName: "suiUSDe"; }; readonly USDsui: { readonly type: "0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI"; readonly decimals: 6; readonly symbol: "USDsui"; readonly displayName: "USDsui"; }; readonly SUI: { readonly type: "0x2::sui::SUI"; readonly decimals: 9; readonly symbol: "SUI"; readonly displayName: "SUI"; }; readonly WAL: { readonly type: "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL"; readonly decimals: 9; readonly symbol: "WAL"; readonly displayName: "WAL"; }; readonly ETH: { readonly type: "0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH"; readonly decimals: 8; readonly symbol: "ETH"; readonly displayName: "suiETH"; }; readonly NAVX: { readonly type: "0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX"; readonly decimals: 9; readonly symbol: "NAVX"; readonly displayName: "NAVX"; }; readonly GOLD: { readonly type: "0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM"; readonly decimals: 6; readonly symbol: "GOLD"; readonly displayName: "XAUM"; }; }; type SupportedAsset = keyof typeof SUPPORTED_ASSETS; type StableAsset = 'USDC' | 'USDsui'; declare const STABLE_ASSETS: readonly StableAsset[]; declare const OPERATION_ASSETS: { readonly send: readonly ["USDC", "USDsui", "SUI"]; readonly swap: "*"; }; type Operation = keyof typeof OPERATION_ASSETS; declare function isAllowedAsset(op: Operation, asset: string): boolean; /** * Throws if the asset is not permitted for the given operation. * * [v4.0 Phase A Day 2] Pre-v4 this allowed `undefined` as a silent default * to USDC. Removed because every write path now requires explicit asset * (see `T2000.send` + `buildSendTx` + `composeTx.send_transfer`). The * `undefined → no-op` branch is kept defensively; the `send_transfer` flow * validates non-undefined. */ declare function assertAllowedAsset(op: Operation, asset: string | undefined): void; /** * [v4.0 Phase A Day 2] Narrow type alias for assets sendable through the * Agent Wallet. Matches `OPERATION_ASSETS.send` exactly. Exported so the * CLI / SDK / composeTx can share one type without re-declaring it. */ type SendableAsset = 'USDC' | 'USDsui' | 'SUI'; declare const SENDABLE_ASSETS: readonly SendableAsset[]; /** * [v4.0 Phase A Day 2] Coin types for the two gasless-allowlisted stables. * Used by `wallet/send.ts` + `composeTx.send_transfer` to construct the * `0x2::balance::send_funds` Move call's `typeArguments`. SUI is excluded * because SUI transfers are NOT gasless (gas-native, uses `tx.gas` split + * `transferObjects` per the existing path). */ declare const GASLESS_STABLE_TYPES: Record<'USDC' | 'USDsui', string>; declare const T2000_OVERLAY_FEE_WALLET: string; declare const DEFAULT_NETWORK: "mainnet"; declare const DEFAULT_GRPC_URL = "https://fullnode.mainnet.sui.io:443"; declare const GASLESS_MIN_STABLE_AMOUNT = 0.01; declare const CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab"; declare const GAS_RESERVE_MIN = 0.05; /** * Shared transaction classifier. * * Consumed by both the SDK's `parseTxRecord` (production agent path) and * the engine's `transaction_history` tool (cold-start RPC path). Keeping * a single source of truth here prevents the two paths from drifting — * see v1.5.3 regression where the SDK path was emitting `action: * 'transaction'` (rendered as "On-chain") while the engine path was * already producing fine-grained labels. */ /** * Coarse action bucket — one of `'send' | 'lending' | 'swap' | * 'transaction'`. Used by the ACI `action` filter on the * `transaction_history` tool. STABLE: downstream queries depend on * exactly these values. * * Order matters: more specific buckets first. Lending patterns precede * swap patterns so a NAVI `::swap` helper (if one ever existed) would * still bucket as lending. */ declare const KNOWN_TARGETS: readonly [RegExp, string][]; /** * Finer-grained display labels — derived from MoveCall function names. * The card renders `label ?? action`, so when this map matches we get * "Deposit" / "Withdraw" / "Borrow" / "Repay" / "Payment link" instead * of the generic "Lending" or "Transaction". * * Order matters: more specific patterns first. Each entry is * (regex, label) where the regex is matched against the * fully-qualified MoveCall target `pkg::module::function`. */ declare const LABEL_PATTERNS: readonly [RegExp, string][]; interface ClassifyBalanceChange { owner: { AddressOwner?: string; } | string; coinType: string; amount: string; } declare function classifyAction(targets: string[], commandTypes: string[]): string; /** * Last-resort fallback when neither `LABEL_PATTERNS` nor the action * bucket produces something useful. * * Returns the first MoveCall's *module* name (e.g. "navi", "spam") so * the card shows something better than the literal word "transaction". * When no MoveCall exists, returns 'on-chain'. * * Note: callers should prefer `classifyLabel` which now layers * pattern-match → coarse action → module name (see commentary there). */ declare function fallbackLabel(targets: string[]): string; /** * Three-tier label resolution for the transaction history card: * 1. Specific keyword match in `LABEL_PATTERNS` ("deposit", * "payment_link", …). * 2. Coarse action bucket from `classifyAction` ("swap", "send", * "lending") — prevents leaking opaque internal module names like * "router" (Cetus aggregator) or "cross_swap" (third-party DEX * aggregators) for txs that we already classified as a swap. * 3. Module name from the first MoveCall (`fallbackLabel`) — only * used when the action bucket itself is the generic "transaction". * * Pre-v0.46.2 we skipped tier 2, so swaps showed labels like "router", * "cross_swap", "scallop_router", etc. instead of the clean "swap". */ declare function classifyLabel(targets: string[], commandTypes: string[]): string; /** * Balance-direction tiebreaker for ambiguous lending calls. * * Many lending modules expose generic entry points (NAVI's bundled * flash actions, `lending_core::*::entry_*`, etc.) that don't carry * a `deposit`/`withdraw`/`borrow`/`repay` keyword in the function * name. When `classifyLabel` falls back to a bare module name like * `"lending"` for a known lending tx, infer direction from the user's * non-SUI balance change: * - net outflow of the supplied asset → deposit (also covers repay, * but repay-without-keyword is essentially never emitted). * - net inflow of the supplied asset → withdraw (also covers borrow). * SUI is excluded so gas-only transactions don't get mislabeled. * * If `LABEL_PATTERNS` matched a specific keyword, the existing label is * returned unchanged. */ declare function refineLendingLabel(currentAction: string, currentLabel: string, moveCallTargets: string[], changes: ClassifyBalanceChange[], address: string): string; interface ClassifyResult { action: string; label: string; } declare function classifyTransaction(moveCallTargets: string[], commandTypes: string[], balanceChanges: ClassifyBalanceChange[], address: string): ClassifyResult; /** * Direction of the user's net non-gas movement for this transaction. * * - `'out'` — the user spent the asset (sends, deposits, repays, * swap-in, payment-link payouts). * - `'in'` — the user received the asset (withdraws, borrows, * swap-out, claims, deposits credited from another wallet). * * Used by the `TransactionHistoryCard` to choose the `+`/`−` sign and * color. Direction is computed from the actual on-chain balance change * — never from the textual label — so opaque action types (`'router'`, * `'cross_swap'`, …) still render the correct sign. */ type TxDirection = 'in' | 'out'; interface ExtractedTransfer { amount?: number; asset?: string; recipient?: string; direction?: TxDirection; } /** * Extracts the principal amount/asset/direction for a transaction * from its `balanceChanges`. * * Algorithm: * 1. Restrict to the user's *own* balance changes. * 2. Prefer non-SUI changes (gas-only SUI deltas are noise). * 3. Pick the change with the largest absolute value — the "principal". * 4. If no non-SUI change exists, fall back to the largest SUI change * so pure-SUI transfers (stake/unstake/native send) still render. * 5. Direction follows the sign of the principal. * 6. Recipient is set only on outflows, by finding a matching inflow * on a *non-user* address with the same coinType. * * Pre-v0.46.2 this function only inspected outflows, so withdraws, * borrows, claims, swap-receives and payment-link receives all * rendered with no amount on the rich card (and with a wrong sign, * because the card guessed direction from the label string). */ declare function extractTransferDetails(changes: ClassifyBalanceChange[] | undefined, sender: string): ExtractedTransfer; /** * Extract every non-zero user balance leg for a transaction — not * just the largest one. Order is RPC order; callers responsible for * any sorting (e.g. audric sorts by USD value once it's priced). * * Sui collapses balance changes by coin type, so a 3-step bundle * touching USDC three times surfaces as ONE leg of net USDC delta. * Distinguishing per-step legs would require parsing the PTB's * commands; this helper deliberately stops at the balance-change * granularity because that's what's reliably available across all * Sui RPC versions. * * Pre-v1.27.2 the only public API was `extractTransferDetails`, * which returned a single "primary" leg and made swap rows * unrenderable (showed `Swapped 987.60 MANIFEST` because MANIFEST * was the largest raw delta even though USDC was the value side). */ declare function extractAllUserLegs(changes: ClassifyBalanceChange[] | undefined, sender: string): TransactionLeg[]; declare function queryHistory(address: string, limit?: number): Promise; declare function queryTransaction(digest: string, senderAddress: string): Promise; /** * Shape of a transaction block as returned by `suix_queryTransactionBlocks` * with `showEffects | showInput | showBalanceChanges` enabled. Exported so * downstream consumers (audric dashboard `/api/history`, `/api/activity`, * etc.) can type their RPC calls without redeclaring the structure. */ interface SuiRpcTxBlock { digest: string; timestampMs?: string; transaction?: unknown; effects?: { gasUsed?: { computationCost: string; storageCost: string; storageRebate: string; }; }; balanceChanges?: ClassifyBalanceChange[]; } /** * Convert a single Sui RPC transaction block to a {@link TransactionRecord} * using the canonical (shared) classifier and balance-change extractor. * * This is the single source of truth for transaction parsing across the * agent-tool path AND the dashboard-API path. Use it instead of writing * a bespoke parser per surface. * * @param tx Raw RPC tx block (must include `effects`, `input`, `balanceChanges`). * @param address Wallet address whose perspective we're parsing from. */ declare function parseSuiRpcTx(tx: SuiRpcTxBlock, address: string): TransactionRecord; /** * Extract the sender (signer) address from a raw RPC tx block. * Returns `null` if the block shape is unexpected. */ declare function extractTxSender(txBlock: unknown): string | null; /** * Extract MoveCall targets (`::::`) and the * sequence of programmable-transaction command types (e.g. `MoveCall`, * `TransferObjects`) from a raw RPC tx block. Tolerates both the * legacy `inner.transactions` field and the newer `inner.commands` * field. */ declare function extractTxCommands(txBlock: unknown): { moveCallTargets: string[]; commandTypes: string[]; }; declare function mistToSui(mist: bigint): number; declare function suiToMist(sui: number): bigint; declare function usdcToRaw(amount: number): bigint; declare function rawToUsdc(raw: bigint): number; declare function stableToRaw(amount: number, decimals: number): bigint; declare function rawToStable(raw: bigint, decimals: number): number; declare function getDecimals(asset: SupportedAsset): number; declare function formatUsd(amount: number): string; declare function formatSui(amount: number): string; declare function formatAssetAmount(amount: number, asset: string): string; /** * Case-insensitive lookup against SUPPORTED_ASSETS keys AND display names. * 'usde' → 'USDe', 'suiusde' → 'USDe', 'suiusdt' → 'USDT', 'usdsui' → 'USDsui'. * Returns the original input if not found so downstream validation can reject it. */ declare function normalizeAsset(input: string): string; interface SimulationResult { success: boolean; gasEstimateSui: number; error?: { moveAbortCode?: number; moveModule?: string; reason: string; rawError: string; }; } declare function simulateTransaction(client: SuiCoreClient, tx: Transaction, sender: string): Promise; declare function throwIfSimulationFailed(sim: SimulationResult): void; /** * Synchronous, network-free preflight for `send`. Validates asset membership, * amount sanity, the gasless stable floor, and recipient address shape — the * cheap checks the v3 host runs before the LLM round-trip / tap-to-confirm. * Returns a `PreflightResult`; never throws. `buildSendTx` calls this first, * then layers the network balance read on top. */ declare function preflightSend(input: { to: string; amount: number; asset: string; }): PreflightResult; /** * Build a PTB that sends `amount` of `asset` from `address` to `to`. * * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A] * * Asset constraint: `'USDC' | 'USDsui' | 'SUI'` only. Other assets throw * `INVALID_ASSET` via `assertAllowedAsset('send', asset)`. The constrained * set matches Sui mainnet's gasless allowlist (USDC + USDsui) plus SUI * for users who want a gas-native transfer. * * Build paths: * - **USDC / USDsui** — `0x2::balance::send_funds` Move call with a * `tx.balance({ type, balance })` input. When built via `SuiGrpcClient`, * the gRPC resolver auto-detects gasless eligibility and zeros gas. * When built via `SuiJsonRpcClient`, the same PTB still executes but * the caller pays normal gas. Minimum 0.01 (protocol allowlist floor). * - **SUI** — `tx.splitCoins(tx.gas, [amount]) → tx.transferObjects()`. * Standard gas-native transfer. No minimum. * * Pre-flight balance check stays on JSON-RPC (`client.getBalance`) — it * sums coin objects + address balance so the legacy `getCoins` page miss * doesn't break for users whose stables landed via gasless deposits. * * `asset` is REQUIRED (no implicit USDC default — pre-v4 hid LLM intent * errors). Callers passing the wrong asset get an explicit error rather * than a silent currency substitution. */ declare function buildSendTx({ client, address, to, amount, asset, }: { client: SuiCoreClient; address: string; to: string; amount: number; asset: SendableAsset; }): Promise; /** * Fragment-appender for the chain-mode send leg of SPEC 7 multi-write * Payment Intents. Consumes a coin reference produced by a previous * appender (e.g. `addWithdrawToTx`, `addSwapToTx`) and transfers it to * `recipient` within the same Payment Intent — no intermediate wallet * materialization. * * Codifies the hand-built send leg from * `scripts/smoke-spec7-withdraw-then-send.ts` (P2.1) into a typed * appender. SPEC 7 § "Layer 1" — P2.2b will register this in the * `WRITE_APPENDER_REGISTRY` under `send_transfer` for chain-mode * dispatch; the registry adapter will handle the wallet-fetch fallback * by delegating to `buildSendTx` when no upstream coin is available. * * For single-step send_transfer flows (no chained predecessor), use * `buildSendTx` directly — it builds a complete tx including the * wallet-coin selection / merge / split prelude. * * [v4.0 Phase A Day 2] Stays on the legacy `transferObjects` path * because chain-mode bundles are NEVER gasless — by definition they * combine multiple Move calls (`withdraw → send`, `swap → send`) which * fail the protocol allowlist check (only `balance::send_funds` and * a few related helpers are eligible). The bundled flow still works, * the user just pays gas (or has it sponsored by audric via Enoki). * * @returns void — the coin is consumed by `tx.transferObjects`. Callers * that need the post-transfer "effective amount" should rely on the * upstream appender's `effectiveAmount` (e.g. `addWithdrawToTx`'s * return), not on this appender. */ declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, recipient: string): void; export { type ZkLoginProof as $, T2000_OVERLAY_FEE_WALLET as A, type BalanceResponse as B, CLOCK_ID as C, DEFAULT_NETWORK as D, ETH_TYPE as E, TOKEN_MAP as F, GAS_RESERVE_MIN as G, type TransactionLeg as H, IKA_TYPE as I, type TransactionRecord as J, KNOWN_TARGETS as K, LABEL_PATTERNS as L, MANIFEST_TYPE as M, NAVX_TYPE as N, OVERLAY_FEE_RATE as O, PREFLIGHT_MAX_AMOUNT as P, type TransactionSigner as Q, type TxDirection as R, STABLE_ASSETS as S, T2000Error as T, USDC_DECIMALS as U, USDC_TYPE as V, USDE_TYPE as W, USDSUI_TYPE as X, USDT_TYPE as Y, WAL_TYPE as Z, WBTC_TYPE as _, COIN_REGISTRY as a, getCoinMeta as a$, ZkLoginSigner as a0, buildSendTx as a1, buildSwapTx as a2, checkPositiveAmount as a3, checkSuiAddress as a4, classifyAction as a5, classifyLabel as a6, classifyTransaction as a7, executeTx as a8, extractAllUserLegs as a9, truncateAddress as aA, usdcToRaw as aB, validateAddress as aC, type T2000Options as aD, type SwapResult as aE, type SwapQuoteResult as aF, type PaymentRequest as aG, type SuiCoreClient as aH, type SponsoredCoinMergeCache as aI, type SendableAsset as aJ, CETUS_USDC_SUI_POOL as aK, type CoinPage as aL, DEFAULT_GRPC_URL as aM, GASLESS_MIN_STABLE_AMOUNT as aN, GASLESS_STABLE_TYPES as aO, OPERATION_ASSETS as aP, type Operation as aQ, SENDABLE_ASSETS as aR, type SelectAndSplitResult as aS, type SerializedCetusRoute as aT, type SerializedCetusRoutePath as aU, type SerializedRouterDataV3 as aV, addSendToTx as aW, addSwapToTx as aX, assertAllowedAsset as aY, deserializeCetusRoute as aZ, fetchAllCoins as a_, extractTransferDetails as aa, extractTxCommands as ab, extractTxSender as ac, fallbackLabel as ad, findSwapRoute as ae, formatAssetAmount as af, formatSui as ag, formatUsd as ah, getDecimals as ai, getDecimalsForCoinType as aj, mapMoveAbortCode as ak, mapWalletError as al, mistToSui as am, parseSuiRpcTx as an, payWithMpp as ao, preflightFail as ap, preflightPay as aq, preflightSend as ar, preflightSwap as as, rawToStable as at, rawToUsdc as au, refineLendingLabel as av, resolveSymbol as aw, resolveTokenType as ax, stableToRaw as ay, suiToMist as az, type ClassifyBalanceChange as b, getSuiClient as b0, getSuiGrpcClient as b1, isAllowedAsset as b2, isCetusRouteFresh as b3, isInRegistry as b4, normalizeAsset as b5, normalizeCoinType as b6, queryHistory as b7, queryTransaction as b8, selectAndSplitCoin as b9, selectSuiCoin as ba, serializeCetusRoute as bb, simulateTransaction as bc, throwIfSimulationFailed as bd, verifyCetusRouteCoinMatch as be, type ClassifyResult as c, type CoinMeta as d, type DepositInfo as e, type ExtractedTransfer as f, KeypairSigner as g, LOFI_TYPE as h, MIST_PER_SUI as i, type OverlayFeeConfig as j, PREFLIGHT_OK as k, type PayOptions as l, type PayResult as m, type PreflightResult as n, SUI_DECIMALS as o, SUI_TYPE as p, SUPPORTED_ASSETS as q, type SendResult as r, type SimulationResult as s, type StableAsset as t, type SuiHolding as u, type SuiRpcTxBlock as v, type SupportedAsset as w, type SwapRouteResult as x, type T2000ErrorCode as y, type T2000ErrorData as z };