@t2000/sdk 4.4.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1222 @@
1
+ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
2
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
3
+ import { SuiClientTypes } from '@mysten/sui/client';
4
+ import { TransactionObjectArgument, Transaction } from '@mysten/sui/transactions';
5
+ import { RouterDataV3 } from '@cetusprotocol/aggregator-sdk';
6
+ import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
7
+
8
+ /**
9
+ * Abstract signing interface that decouples the SDK from any specific
10
+ * key management strategy (Ed25519 keypair, zkLogin, multisig, …).
11
+ */
12
+ interface TransactionSigner {
13
+ getAddress(): string;
14
+ signTransaction(txBytes: Uint8Array): Promise<{
15
+ signature: string;
16
+ }>;
17
+ /**
18
+ * Sign an arbitrary personal message. Required by `@suimpp/mpp` 0.7+ for
19
+ * grief-protection proofs (sender identity verification on settled payments).
20
+ */
21
+ signPersonalMessage(messageBytes: Uint8Array): Promise<{
22
+ signature: string;
23
+ bytes?: string;
24
+ }>;
25
+ }
26
+
27
+ declare class KeypairSigner implements TransactionSigner {
28
+ private readonly keypair;
29
+ constructor(keypair: Ed25519Keypair);
30
+ getAddress(): string;
31
+ signTransaction(txBytes: Uint8Array): Promise<{
32
+ signature: string;
33
+ }>;
34
+ signPersonalMessage(messageBytes: Uint8Array): Promise<{
35
+ signature: string;
36
+ bytes: string;
37
+ }>;
38
+ /** Access the underlying keypair for APIs that still require it directly. */
39
+ getKeypair(): Ed25519Keypair;
40
+ }
41
+
42
+ interface ZkLoginProof {
43
+ proofPoints: {
44
+ a: string[];
45
+ b: string[][];
46
+ c: string[];
47
+ };
48
+ issBase64Details: {
49
+ indexMod4: number;
50
+ value: string;
51
+ };
52
+ headerBase64: string;
53
+ addressSeed: string;
54
+ }
55
+ declare class ZkLoginSigner implements TransactionSigner {
56
+ private readonly ephemeralKeypair;
57
+ private readonly zkProof;
58
+ private readonly userAddress;
59
+ private readonly maxEpoch;
60
+ constructor(ephemeralKeypair: Ed25519Keypair, zkProof: ZkLoginProof, userAddress: string, maxEpoch: number);
61
+ getAddress(): string;
62
+ signTransaction(txBytes: Uint8Array): Promise<{
63
+ signature: string;
64
+ }>;
65
+ signPersonalMessage(messageBytes: Uint8Array): Promise<{
66
+ signature: string;
67
+ bytes: string;
68
+ }>;
69
+ isExpired(currentEpoch: number): boolean;
70
+ }
71
+
72
+ /**
73
+ * [gRPC migration / S.438] Transport-agnostic client type for the migration.
74
+ *
75
+ * Both `SuiJsonRpcClient` and `SuiGrpcClient` extend `BaseClient` and expose
76
+ * an identical `.core.*` API. During the migration, call sites are rewritten
77
+ * from legacy methods (`getBalance`, `getObject`, `executeTransactionBlock`)
78
+ * to the unified `client.core.*` API and typed against this union — so the
79
+ * rewrite is behavior-preserving while still on JSON-RPC, and the final
80
+ * transport flip (gRPC) is a one-line change in `getSuiClient()`.
81
+ *
82
+ * The query surface that has no gRPC `core` equivalent (`queryTransactionBlocks`
83
+ * / `queryEvents`, Stage 0 finding A) uses `getSuiGraphQLClient()` instead.
84
+ */
85
+ type SuiCoreClient = SuiJsonRpcClient | SuiGrpcClient;
86
+ /**
87
+ * [gRPC cutover] The canonical agent client is now gRPC. Mysten deactivates
88
+ * the JSON-RPC fullnode interface on 2026-07-31, so reads (`.core.*`) and
89
+ * execution route over gRPC — transaction *builds* already do
90
+ * (`getSuiGrpcClient`). The whole SDK was first refactored onto the unified
91
+ * `.core` API (behavior-preserving on JSON-RPC), then flipped here. The
92
+ * `rpcUrl` arg / `T2000_RPC_URL` override now resolve a gRPC `baseUrl`
93
+ * (`DEFAULT_RPC_URL` is the same host as `DEFAULT_GRPC_URL`). The historical
94
+ * query surface stays on `getSuiGraphQLClient()`.
95
+ */
96
+ declare function getSuiClient(rpcUrl?: string): SuiGrpcClient;
97
+ /**
98
+ * Cached `SuiGrpcClient` for gasless stablecoin transfer builds.
99
+ *
100
+ * [v4.0 Phase A Day 2 — SPEC_AGENT_WALLET_GREENFIELD §A]
101
+ *
102
+ * Why this exists: Sui mainnet's protocol-level gasless stablecoin transfers
103
+ * (`0x2::balance::send_funds` on the USDC + USDsui allowlist) are detected
104
+ * ONLY when the transaction is built through a `SuiGrpcClient`. The gRPC
105
+ * client's build resolver inspects the PTB at `tx.build()` time and, if it
106
+ * matches the gasless pattern, sets `gasPrice=0` + `gasBudget=0` automatically.
107
+ * Building the SAME PTB through `SuiJsonRpcClient` produces the same bytes
108
+ * but with non-zero gas — the tx still works, but the user pays SUI gas.
109
+ *
110
+ * Execution stays on JSON-RPC (`SuiJsonRpcClient.executeTransactionBlock`)
111
+ * because (a) the rest of the SDK expects JSON-RPC and (b) Sui's docs
112
+ * explicitly support a "build via gRPC, execute via JSON-RPC" hybrid:
113
+ * https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers
114
+ *
115
+ * Override the endpoint with the `T2000_GRPC_URL` env var or the
116
+ * `grpcUrl` arg. Cache is keyed by URL so multiple endpoints can
117
+ * co-exist (e.g., the rare testnet smoke + production usage from the
118
+ * same process).
119
+ */
120
+ declare function getSuiGrpcClient(grpcUrl?: string): SuiGrpcClient;
121
+ declare function validateAddress(address: string): string;
122
+ declare function truncateAddress(address: string): string;
123
+ /**
124
+ * Normalize a Sui coin type to its canonical long-form 64-hex address.
125
+ * `0x2::sui::SUI` → `0x0000…0002::sui::SUI`. Idempotent on already-long
126
+ * forms. Returns the input unchanged if it doesn't look like a coin type
127
+ * (`<address>::<module>::<name>`) so callers can pass arbitrary strings
128
+ * without crashing.
129
+ *
130
+ * Why this exists: BlockVision's `/v2/sui/coin/price/list` endpoint
131
+ * silently returns an empty `prices` map for short-form coin types
132
+ * (notably `0x2::sui::SUI` — the native gas coin). Internal callers must
133
+ * pass the long form, but external callers (LLM tool args, cached
134
+ * coin-type strings, audit logs) commonly use the short form. Normalize
135
+ * before the network call, denormalize back to the caller's input shape
136
+ * after, and short/long become interchangeable.
137
+ */
138
+ declare function normalizeCoinType(coinType: string): string;
139
+
140
+ /**
141
+ * Wallet-side coin selection helpers — single source of truth for the
142
+ * "produce a `Coin<T>` argument holding `amount` raw units of `coinType`,
143
+ * owned by `address`" pattern. Used by every wallet-mode appender that
144
+ * needs a coin input (save, send, swap, repay, stake, etc.).
145
+ *
146
+ * **2026-05-22 — address-balance migration.** Sui mainnet's address-balance
147
+ * feature ships funds account-style instead of as discrete `Coin<T>` objects.
148
+ * After a payment via `0x2::balance::send_funds`, the leftover lands in the
149
+ * sender's address balance with a synthetic "coin reservation" representing
150
+ * the deposit. `client.getCoins()` correctly filters those reservations out
151
+ * (they aren't real owned objects), so the old fetch+merge+split pattern
152
+ * threw `INSUFFICIENT_BALANCE` for users whose stables had drifted into
153
+ * address balance — even when `getBalance().totalBalance` showed plenty.
154
+ *
155
+ * The fix is structural: hand the work to `coinWithBalance({ type, balance })`
156
+ * from `@mysten/sui/transactions`. Its build-time resolver inspects coins +
157
+ * address balance together (`getBalance` + `listCoins`), then emits the
158
+ * right shape — direct `redeem_funds` from address balance when AB ≥
159
+ * required, or merge-and-split across coins + AB withdrawal when not. Multi
160
+ * intents per coin type get batched into a single merge in one PTB, so the
161
+ * old per-PTB merge cache is no longer needed.
162
+ *
163
+ * Pre-flight uses `client.getBalance().totalBalance` (sums coins + AB)
164
+ * instead of summing the paginated `getCoins` page. That's the OTHER half
165
+ * of the migration — the legacy path could see `0` from `getCoins` and
166
+ * mistakenly throw before `coinWithBalance` ever ran.
167
+ */
168
+
169
+ interface CoinPage {
170
+ ids: string[];
171
+ totalBalance: bigint;
172
+ }
173
+ /**
174
+ * Sum every coin of `coinType` owned by `owner`, INCLUDING address balance.
175
+ * Returns the IDs of any discrete coin objects that exist (callers
176
+ * occasionally need this for non-`coinWithBalance` paths, e.g. SUIns name
177
+ * registration which expects raw object IDs).
178
+ *
179
+ * Pre-2026-05-22 this function paginated `client.getCoins` and summed
180
+ * the page balances. That misses address-balance funds (the SDK filters
181
+ * them out of `getCoins` for back-compat). The new implementation calls
182
+ * `getBalance` for the canonical total and `getCoins` for the optional
183
+ * ID list — both round-trips, but they happen in parallel.
184
+ */
185
+ declare function fetchAllCoins(client: SuiCoreClient, owner: string, coinType: string): Promise<CoinPage>;
186
+ interface SelectAndSplitResult {
187
+ /** TransactionObjectArgument for a coin holding `effectiveAmount` raw units. */
188
+ coin: TransactionObjectArgument;
189
+ /** Actual raw amount the returned coin holds. May be < requested if `swapAll` is true. */
190
+ effectiveAmount: bigint;
191
+ /** True iff the request consumed the entire wallet balance (no split needed). */
192
+ swapAll: boolean;
193
+ }
194
+ /**
195
+ * Wallet-mode coin selection prelude. Pre-flights against
196
+ * `getBalance().totalBalance` (coins + address balance combined), then
197
+ * returns a `coinWithBalance({ type, balance })` argument that the
198
+ * `@mysten/sui` resolver fulfills at build time.
199
+ *
200
+ * Throws `T2000Error` (`INSUFFICIENT_BALANCE`) when:
201
+ * - `amount` is bigger than the total balance AND the caller did NOT
202
+ * opt into `swapAll: true` clipping.
203
+ * - `amount === 'all'` AND total balance is zero.
204
+ *
205
+ * @param tx — PTB to register the `coinWithBalance` intent against.
206
+ * @param client — Sui RPC client for the pre-flight `getBalance` lookup.
207
+ * @param owner — wallet address whose coins to source from.
208
+ * @param coinType — fully-qualified Sui coin type (e.g. `"0x...::usdc::USDC"`).
209
+ * @param amount — raw amount to source (in MIST / smallest unit). Pass
210
+ * `'all'` to consume the entire balance.
211
+ * @param options.allowSwapAll — if true (default), `amount` >= totalBalance
212
+ * auto-clips to total. If false, throws when the request would over-consume.
213
+ * @param options.sponsoredContext — when true, source ONLY from discrete coin
214
+ * objects (never the address balance). See the long note below — this exists
215
+ * because Enoki's gas station can't yet deserialize a `TransactionData` that
216
+ * contains the address-balance `FundsWithdrawal` reservation that
217
+ * `coinWithBalance` emits. Self-funded callers leave this false: the fullnode
218
+ * handles `FundsWithdrawal` fine, so the address-balance path is preferred
219
+ * (it can reach funds that aren't held as coin objects).
220
+ *
221
+ * @returns
222
+ * - `coin` — `TransactionObjectArgument` ready for downstream consumption.
223
+ * - `effectiveAmount` — the raw amount the returned coin holds (handles
224
+ * swapAll clipping).
225
+ * - `swapAll` — true iff the entire balance was consumed.
226
+ */
227
+ declare function selectAndSplitCoin(tx: Transaction, client: SuiCoreClient, owner: string, coinType: string, amount: bigint | 'all', options?: {
228
+ allowSwapAll?: boolean;
229
+ sponsoredContext?: boolean;
230
+ mergeCache?: SponsoredCoinMergeCache;
231
+ }): Promise<SelectAndSplitResult>;
232
+ /**
233
+ * Coin-object-only selection for sponsored (Enoki) transactions. Fetches the
234
+ * owner's discrete `Coin<T>` objects (NOT the address balance — `getCoins`
235
+ * excludes it), merges them, and splits the requested amount. Never emits a
236
+ * `FundsWithdrawal` reservation, so the resulting `TransactionData` stays on
237
+ * the shape Enoki's gas station can serialize.
238
+ *
239
+ * Throws `ADDRESS_BALANCE_UNSPONSORABLE` when the coin objects don't cover the
240
+ * request — which, for a user whose `getBalance().totalBalance` shows funds,
241
+ * means those funds live in the address balance (e.g. received via a gasless
242
+ * stablecoin transfer) and can't be moved through a sponsored transaction yet.
243
+ */
244
+ /**
245
+ * Per-PTB cache of merged sponsored coin-object primaries, keyed by coin
246
+ * type. The FIRST `selectCoinObjectsOnly` call for a given coin type in a
247
+ * PTB fetches the owner's discrete `Coin<T>` objects, merges them into one
248
+ * `primary`, and records it here alongside the remaining (unspent) balance.
249
+ * EVERY subsequent leg sourcing the same coin type splits from that cached
250
+ * `primary` instead of re-fetching + re-merging.
251
+ *
252
+ * Why this exists (S.xxx, 2026-06-02): a sponsored bundle with 2+ legs
253
+ * sourcing the same coin (e.g. `SUI→WAL` + `SUI→DEEP`, or `swap USDC` +
254
+ * `save USDC`) called `selectCoinObjectsOnly` once per leg. Each call
255
+ * emitted its own `mergeCoins` over the SAME coin objects, so the second
256
+ * leg's merge referenced coins the first leg already consumed → Enoki
257
+ * dry-run failed with `CommandArgumentError { ArgumentWithoutValue }`.
258
+ *
259
+ * This is NOT SUI-specific. Under sponsorship, `selectAndSplitCoin` routes
260
+ * EVERY asset through `selectCoinObjectsOnly` (the `coinWithBalance`
261
+ * batching that would otherwise dedup these merges only runs for
262
+ * non-sponsored CLI/direct flows — its address-balance `FundsWithdrawal`
263
+ * reservation is what Enoki can't deserialize, issue #93). So the cache is
264
+ * the dedup layer for ALL coin types in a sponsored multi-leg PTB, keyed
265
+ * by coin type. SUI was simply the first asset observed failing in the
266
+ * wild because it's the most common swap source.
267
+ */
268
+ type SponsoredCoinMergeCache = Map<string, {
269
+ primary: TransactionObjectArgument;
270
+ remaining: bigint;
271
+ }>;
272
+ /**
273
+ * SUI-specific coin selection. Branches on sponsorship context:
274
+ *
275
+ * - **Self-funded (`sponsoredContext: false`)** — splits from `tx.gas`
276
+ * directly (the user's gas coin IS their SUI). More efficient — no
277
+ * `getBalance` RTT.
278
+ *
279
+ * - **Sponsored (`sponsoredContext: true`)** — sources from the user's
280
+ * discrete SUI coin objects (`selectCoinObjectsOnly`). This both (a) avoids
281
+ * `tx.gas`, which belongs to the Enoki sponsor — NOT the user — under
282
+ * sponsored flows (the original S.260 reason for `useGasCoin: false`), AND
283
+ * (b) avoids `coinWithBalance`'s address-balance `FundsWithdrawal`, which
284
+ * Enoki's gas station can't deserialize (issue #93). If the user's SUI is
285
+ * address-balance-only, it raises `ADDRESS_BALANCE_UNSPONSORABLE`.
286
+ */
287
+ declare function selectSuiCoin(tx: Transaction, client: SuiCoreClient, owner: string, amountMist: bigint, sponsoredContext: boolean, mergeCache?: SponsoredCoinMergeCache): Promise<SelectAndSplitResult>;
288
+
289
+ /**
290
+ * Unified token registry — single source of truth for coin types, decimals,
291
+ * and symbols. ZERO heavy dependencies; safe to import anywhere (server,
292
+ * browser, Edge).
293
+ *
294
+ * Used for swap-by-symbol resolution, amount formatting (decimals), and
295
+ * history/balance display. USDC is the settlement stable (send / receive /
296
+ * x402 pay); everything else is holdable / swappable. There is no curated
297
+ * "tier" gate — swaps accept any coin type (Cetus routes); the registry just
298
+ * makes the common tokens ergonomic to reference by symbol.
299
+ *
300
+ * To add a token: add ONE entry to COIN_REGISTRY below — everything derives
301
+ * from it.
302
+ */
303
+ interface CoinMeta {
304
+ type: string;
305
+ decimals: number;
306
+ symbol: string;
307
+ }
308
+ /**
309
+ * Canonical coin registry.
310
+ * Key = user-friendly name (used in swap, CLI, prompts).
311
+ */
312
+ declare const COIN_REGISTRY: Record<string, CoinMeta>;
313
+ /**
314
+ * Returns the registry metadata for a coin type, or `undefined` if the coin
315
+ * is not in the registry. Used for canonical-symbol + decimal resolution.
316
+ */
317
+ declare function getCoinMeta(coinType: string): CoinMeta | undefined;
318
+ /**
319
+ * Returns true if the coin type appears in COIN_REGISTRY. Useful as a
320
+ * canonical-symbol gate — e.g. so the registry's mixed-case `USDsui` wins
321
+ * over a vendor feed's uppercase `USDSUI`.
322
+ */
323
+ declare function isInRegistry(coinType: string): boolean;
324
+ /**
325
+ * Get decimals for any coin type. Checks full type match, then suffix match,
326
+ * then defaults to 9. Works for any token, registered or not.
327
+ */
328
+ declare function getDecimalsForCoinType(coinType: string): number;
329
+ /**
330
+ * Resolve a full coin type to a user-friendly symbol.
331
+ * Returns the last `::` segment if not in the registry.
332
+ */
333
+ declare function resolveSymbol(coinType: string): string;
334
+ /**
335
+ * Name → type map for swap resolution. Derived from COIN_REGISTRY.
336
+ * Contains BOTH original-case and UPPERCASE keys for case-insensitive lookup.
337
+ */
338
+ declare const TOKEN_MAP: Record<string, string>;
339
+ /**
340
+ * Resolve a user-friendly token name to its full coin type.
341
+ * Returns the input unchanged if already a full coin type (contains "::").
342
+ * Case-insensitive: 'usde', 'USDe', 'USDE' all resolve correctly.
343
+ */
344
+ declare function resolveTokenType(nameOrType: string): string | null;
345
+ /** Common type constants for direct import. */
346
+ declare const SUI_TYPE: string;
347
+ declare const USDC_TYPE: string;
348
+ declare const USDT_TYPE: string;
349
+ declare const USDSUI_TYPE: string;
350
+ declare const USDE_TYPE: string;
351
+ declare const ETH_TYPE: string;
352
+ declare const WBTC_TYPE: string;
353
+ declare const WAL_TYPE: string;
354
+ declare const NAVX_TYPE: string;
355
+ declare const IKA_TYPE: string;
356
+ declare const LOFI_TYPE: string;
357
+ declare const MANIFEST_TYPE: string;
358
+
359
+ /**
360
+ * Cetus Aggregator V3 SDK wrapper — the ONLY file that imports @cetusprotocol/aggregator-sdk.
361
+ * Documented CLAUDE.md exception: multi-DEX routing cannot be feasibly replaced by thin tx builders.
362
+ *
363
+ * [B5 v2 / @t2000/sdk@1.1.0 / 2026-04-30]
364
+ * Overlay fee config is now per-call instead of a module-level singleton. CLI / direct
365
+ * SDK callers (`T2000.swap()`) DON'T pass `overlayFee` → fee-free swap. Audric's
366
+ * prepare/route.ts ALWAYS passes `overlayFee = { rate: OVERLAY_FEE_RATE, receiver:
367
+ * T2000_OVERLAY_FEE_WALLET }` → fee charged. Structural inclusion (Audric's code can't
368
+ * forget to pass it because it IS the code), not a toggle that defaults to safe.
369
+ *
370
+ * Pre-1.1.0: a module-level `OVERLAY_FEE_RECEIVER` constant defaulted to a Move object
371
+ * ID. USDC sent there became OwnedObjects keyed to the object and was inaccessible.
372
+ * Fixed by making the receiver a regular wallet address (T2000_OVERLAY_FEE_WALLET) AND
373
+ * by removing the singleton pattern that hid the misconfig.
374
+ */
375
+
376
+ interface OverlayFeeConfig {
377
+ /** Fee rate as a fraction (e.g. 0.001 = 0.1%). Pass 0 to disable. */
378
+ rate: number;
379
+ /** Wallet address that receives the overlay fee. */
380
+ receiver: string;
381
+ }
382
+ interface SwapRouteResult {
383
+ routerData: RouterDataV3;
384
+ amountIn: string;
385
+ amountOut: string;
386
+ byAmountIn: boolean;
387
+ priceImpact: number;
388
+ insufficientLiquidity: boolean;
389
+ }
390
+ interface SerializedCetusRoutePath {
391
+ id: string;
392
+ direction: boolean;
393
+ provider: string;
394
+ from: string;
395
+ target: string;
396
+ feeRate: number;
397
+ amountIn: string;
398
+ amountOut: string;
399
+ version?: string;
400
+ publishedAt?: string;
401
+ extendedDetails?: Record<string, unknown>;
402
+ }
403
+ interface SerializedRouterDataV3 {
404
+ quoteID?: string;
405
+ /** RouterDataV3.amountIn (BN) → decimal string */
406
+ amountIn: string;
407
+ /** RouterDataV3.amountOut (BN) → decimal string */
408
+ amountOut: string;
409
+ byAmountIn: boolean;
410
+ paths: SerializedCetusRoutePath[];
411
+ insufficientLiquidity: boolean;
412
+ deviationRatio: number;
413
+ /** RouterDataV3.packages (Map) → Record */
414
+ packages?: Record<string, string>;
415
+ totalDeepFee?: number;
416
+ error?: {
417
+ code: number;
418
+ msg: string;
419
+ };
420
+ overlayFee?: number;
421
+ }
422
+ interface SerializedCetusRoute {
423
+ routerData: SerializedRouterDataV3;
424
+ amountIn: string;
425
+ amountOut: string;
426
+ byAmountIn: boolean;
427
+ priceImpact: number;
428
+ insufficientLiquidity: boolean;
429
+ /**
430
+ * Wall-clock timestamp (ms since epoch) at which the route was discovered.
431
+ * Used by audric's prepare-route for SPEC 20.2 D-3 TTL re-validation: if
432
+ * the route is older than the threshold AND price impact has shifted
433
+ * beyond tolerance, fall back to a fresh `findSwapRoute()` call.
434
+ */
435
+ discoveredAt: number;
436
+ /**
437
+ * Snapshot of the input/output coin types the route was discovered for.
438
+ * SPEC 20.2 D-2 (b) structural verification: prepare-route asserts
439
+ * input/output coins match before using the fast-path; mismatch falls
440
+ * back to fresh discovery (defense against client-side tampering and
441
+ * against legitimate token-type drift in the request).
442
+ */
443
+ fromCoinType: string;
444
+ toCoinType: string;
445
+ }
446
+ declare function serializeCetusRoute(route: SwapRouteResult, context: {
447
+ fromCoinType: string;
448
+ toCoinType: string;
449
+ }): SerializedCetusRoute;
450
+ declare function deserializeCetusRoute(serialized: SerializedCetusRoute): SwapRouteResult;
451
+ /**
452
+ * SPEC 20.2 D-2 (b) structural verification helper. Returns true when the
453
+ * serialized route matches the requested coin types (i.e. it's safe to use
454
+ * as the prepare-route fast-path), false otherwise (tampered, or input
455
+ * mismatch from a legitimate but stale pending action). Caller falls back
456
+ * to a fresh `findSwapRoute()` call when verification fails.
457
+ */
458
+ declare function verifyCetusRouteCoinMatch(serialized: SerializedCetusRoute, expected: {
459
+ fromCoinType: string;
460
+ toCoinType: string;
461
+ }): boolean;
462
+ /**
463
+ * SPEC 20.2 D-3 (b) TTL helper. Returns true when the serialized route is
464
+ * fresh enough to use as the fast-path (< `maxAgeMs` old). Returns false
465
+ * for stale routes — caller falls back to fresh `findSwapRoute()` to pick
466
+ * up any pool-price drift since route discovery.
467
+ *
468
+ * Default 30s aligns with the existing quote-freshness contract surfaced
469
+ * to users via `pending_action.quoteAge` (the PermissionCard "QUOTE Ns OLD"
470
+ * badge starts warning the user past 30s).
471
+ */
472
+ declare function isCetusRouteFresh(serialized: SerializedCetusRoute, maxAgeMs?: number): boolean;
473
+ /**
474
+ * Default Audric swap overlay fee — 0.1%. Exported for consumers that want to use
475
+ * the canonical Audric rate (the Audric prepare-route does this). Changing this
476
+ * rate requires a coordinated SDK + audric release.
477
+ */
478
+ declare const OVERLAY_FEE_RATE = 0.001;
479
+ /**
480
+ * Find the optimal swap route via Cetus Aggregator REST API.
481
+ *
482
+ * Pass `overlayFee` to charge an overlay fee on the output (Audric's pattern).
483
+ * Omit it for a fee-free swap (CLI / direct SDK pattern).
484
+ */
485
+ declare function findSwapRoute(params: {
486
+ walletAddress: string;
487
+ from: string;
488
+ to: string;
489
+ amount: bigint;
490
+ byAmountIn: boolean;
491
+ overlayFee?: OverlayFeeConfig;
492
+ /**
493
+ * Optional Cetus provider allow-list. When omitted, all 30+ DEXes
494
+ * are eligible. Sponsored flows (Enoki) MUST pass an exclusion list
495
+ * computed via `getProvidersExcluding([...])` from the Cetus SDK to
496
+ * remove Pyth-dependent providers (HAEDALPMM, METASTABLE, OBRIC,
497
+ * STEAMM_OMM, STEAMM_OMM_V2, SEVENK, HAEDALHMMV2) — those reference
498
+ * `tx.gas` for oracle fees, which Enoki rejects in sponsored txs.
499
+ * Non-sponsored callers (CLI, direct SDK) leave this undefined.
500
+ */
501
+ providers?: string[];
502
+ }): Promise<SwapRouteResult | null>;
503
+ /**
504
+ * Build a swap PTB from a route result. The caller must provide an input coin
505
+ * obtained by splitting/merging wallet coins.
506
+ *
507
+ * **Important:** Cetus's `routerSwap` reads the overlay-fee config from the
508
+ * AggregatorClient instance. The `overlayFee` param here MUST match the one
509
+ * passed to `findSwapRoute` for the same swap (otherwise you'll hit the cache
510
+ * boundary and get a different client with different overlay config).
511
+ */
512
+ declare function buildSwapTx(params: {
513
+ walletAddress: string;
514
+ route: SwapRouteResult;
515
+ tx: Transaction;
516
+ inputCoin: TransactionObjectArgument;
517
+ slippage: number;
518
+ overlayFee?: OverlayFeeConfig;
519
+ }): Promise<TransactionObjectArgument>;
520
+ /**
521
+ * Append a swap fragment to an existing PTB. SPEC 7 § "Layer 1" Cetus
522
+ * appender. Two modes, dispatched by the presence of `input.inputCoin`:
523
+ *
524
+ * - **Wallet mode** (`inputCoin` omitted) — sources `from`-asset funds
525
+ * via `coinWithBalance({ type, balance })` (resolves coin objects +
526
+ * address balance at build time), runs the swap. Mirrors the audric
527
+ * host's `transactions/prepare/route.ts` swap branch (P2.2c will
528
+ * retire that branch in favor of this appender via `composeTx`).
529
+ *
530
+ * - **Chain mode** (`inputCoin` provided) — consumes the passed-in coin
531
+ * reference (typically produced by an upstream appender like
532
+ * `addWithdrawToTx`) directly, no wallet fetch / no merge / no
533
+ * split. This is the SPEC 7 multi-write enabler ("withdraw → swap →
534
+ * save" without intermediate wallet materialization).
535
+ *
536
+ * **SUI in wallet mode:** ALWAYS sources through `selectSuiCoin` (which
537
+ * routes via `coinWithBalance({ type: SUI, useGasCoin: false })` under
538
+ * sponsored flows, OR `tx.splitCoins(tx.gas, ...)` under self-funded
539
+ * flows). The caller MUST set `sponsoredContext` correctly — otherwise
540
+ * sponsored swaps with SUI source fail with `Cannot use GasCoin as a
541
+ * transaction argument` (Enoki owns `tx.gas`, the PTB body referencing
542
+ * it as an argument is invalid for sponsorship). 2.14.0 shipped without
543
+ * this branch and broke audric/web-v2 SUI→USDC swaps; restored in 2.14.1
544
+ * (S.260). For non-sponsored flows (CLI), `T2000.swap()` pre-builds the
545
+ * inputCoin via `tx.splitCoins(tx.gas, [rawAmount])[0]` and uses chain
546
+ * mode, sidestepping wallet-mode entirely — this branch is a defensive
547
+ * safety net for future direct SDK users who pass SUI in wallet mode.
548
+ *
549
+ * **`swapAll` semantics (wallet mode):** if the requested raw amount
550
+ * is >= the wallet's total `from` balance, the appender consumes the
551
+ * entire merged primary coin (not a split), matching audric's host
552
+ * route's `swapAll` clipping. The returned `effectiveAmountIn` reflects
553
+ * the actual consumed amount in display units.
554
+ *
555
+ * **Slippage:** clamped to [0.001, 0.05] (0.1% – 5%). Defaults to 0.01.
556
+ *
557
+ * @returns
558
+ * - `coin` — output coin reference, ready for downstream consumption
559
+ * (e.g. `addSaveToTx`) or wallet transfer (`tx.transferObjects`).
560
+ * - `effectiveAmountIn` — display-units input amount the swap actually
561
+ * consumes (handles `swapAll` clipping in wallet mode; in chain mode
562
+ * echoes the requested `input.amount`).
563
+ * - `expectedAmountOut` — display-units output amount per the route
564
+ * quote. Actual on-chain output may differ within slippage.
565
+ * - `route` — raw `SwapRouteResult` for downstream telemetry / logging.
566
+ */
567
+ declare function addSwapToTx(tx: Transaction, client: SuiCoreClient, address: string, input: {
568
+ from: string;
569
+ to: string;
570
+ amount: number;
571
+ slippage?: number;
572
+ byAmountIn?: boolean;
573
+ overlayFee?: OverlayFeeConfig;
574
+ inputCoin?: TransactionObjectArgument;
575
+ /**
576
+ * Optional Cetus provider allow-list. Forwarded to `findSwapRoute`.
577
+ * Sponsored flows (Enoki) MUST pass `getProvidersExcluding([...])`
578
+ * to remove Pyth-dependent providers — see `findSwapRoute`'s JSDoc
579
+ * for the exclusion list. Non-sponsored callers omit this.
580
+ */
581
+ providers?: string[];
582
+ /**
583
+ * [SPEC 20.2 D-3 (b)] Precomputed route from a prior `findSwapRoute()`
584
+ * call (typically captured by `swap_quote` and threaded through
585
+ * `pending_action.cetusRoute`). When present AND not stale (per
586
+ * `isCetusRouteFresh`) AND the input/output coins match, this skips
587
+ * the ~400-500ms `findSwapRoute()` discovery call. Stale routes are
588
+ * silently ignored (caller falls back to fresh discovery).
589
+ *
590
+ * Caller responsibility: pass the SAME `overlayFee` / `providers` /
591
+ * `byAmountIn` that produced the precomputed route. Mismatch will
592
+ * still produce a working swap but may use the wrong overlay-fee
593
+ * config (the route data already encodes the chosen DEX path).
594
+ */
595
+ precomputedRoute?: SwapRouteResult;
596
+ /**
597
+ * Whether this swap is being built inside a sponsored-tx flow (Enoki)
598
+ * vs self-funded (CLI / direct sign). Load-bearing for SUI-source
599
+ * swaps in wallet mode: under sponsored flows, `tx.gas` belongs to
600
+ * the sponsor and CANNOT be referenced as a transaction argument
601
+ * (Sui protocol rejects with `Cannot use GasCoin as a transaction
602
+ * argument`). When `true`, SUI source routes through `selectSuiCoin`
603
+ * with `useGasCoin: false` so the resolver sources from the user's
604
+ * SUI coin objects + address balance instead. Defaults to `false`
605
+ * (back-compat — pre-2.14.1 behavior). Audric/web-v2's compose path
606
+ * threads this through via `composeTx({ sponsoredContext: true })`.
607
+ */
608
+ sponsoredContext?: boolean;
609
+ /**
610
+ * Per-PTB merge cache for sponsored coin-object sourcing (any coin
611
+ * type — SUI in the dedicated branch, USDC/USDsui/etc. in the wallet
612
+ * branch). Provided by `composeTx`'s orchestration loop so multiple
613
+ * legs sourcing the same coin in one bundle share a single merged
614
+ * primary coin instead of each emitting its own `mergeCoins` (the
615
+ * second of which references already-consumed coins → Enoki dry-run
616
+ * `ArgumentWithoutValue`). Single swaps don't need it; omit. See
617
+ * `SponsoredCoinMergeCache` JSDoc.
618
+ */
619
+ coinMergeCache?: SponsoredCoinMergeCache;
620
+ }): Promise<{
621
+ coin: TransactionObjectArgument;
622
+ effectiveAmountIn: number;
623
+ expectedAmountOut: number;
624
+ route: SwapRouteResult;
625
+ /** True when `precomputedRoute` was used (no `findSwapRoute()` call). */
626
+ usedPrecomputedRoute: boolean;
627
+ }>;
628
+
629
+ interface T2000Options {
630
+ keyPath?: string;
631
+ /** PIN to decrypt the key file. Accepts any string (4+ chars). */
632
+ pin?: string;
633
+ /** @deprecated Use `pin` instead. */
634
+ passphrase?: string;
635
+ network?: 'mainnet' | 'testnet';
636
+ rpcUrl?: string;
637
+ }
638
+ interface SuiHolding {
639
+ /** SUI balance in whole SUI (not MIST). */
640
+ amount: number;
641
+ /** USD value of the SUI holding. */
642
+ usdValue: number;
643
+ }
644
+ interface BalanceResponse {
645
+ /** Spendable stablecoins keyed by symbol (USDC, USDsui) — gasless to send/pay. */
646
+ stables: Record<string, number>;
647
+ /** Sum of spendable stables in USD. Used for send/pay pre-checks. */
648
+ available: number;
649
+ /** SUI holding — used for swaps (and any non-gasless gas). Not a "reserve". */
650
+ sui: SuiHolding;
651
+ /** Total wallet value in USD (available + sui.usdValue). */
652
+ totalUsd: number;
653
+ }
654
+ interface SendResult {
655
+ success: boolean;
656
+ tx: string;
657
+ amount: number;
658
+ to: string;
659
+ /**
660
+ * [S.279] Set when the recipient was resolved via SuiNS (e.g. `alex.sui`).
661
+ * CLI receipts render "Sent to alex.sui (0xabc...)" when present.
662
+ */
663
+ suinsName?: string;
664
+ gasCost: number;
665
+ gasCostUnit: string;
666
+ balance: BalanceResponse;
667
+ }
668
+ interface DepositInfo {
669
+ address: string;
670
+ network: string;
671
+ supportedAssets: string[];
672
+ instructions: string;
673
+ }
674
+ interface PaymentRequest {
675
+ address: string;
676
+ network: string;
677
+ amount: number | null;
678
+ currency: string;
679
+ memo: string | null;
680
+ label: string | null;
681
+ /** Unique payment identifier (UUID) for Payment Kit registry */
682
+ nonce: string;
683
+ /** Payment Kit URI (sui:pay?...) for QR codes and wallet deep links */
684
+ qrUri: string;
685
+ /** Human-readable summary */
686
+ displayText: string;
687
+ }
688
+ /**
689
+ * One non-zero user balance change for a transaction. Sui collapses
690
+ * balance changes by coin type, so a 3-step bundle that touches USDC
691
+ * three times surfaces as ONE leg of net USDC delta — not three.
692
+ *
693
+ * [Activity rebuild / 2026-05-10] Added so consumers can render swap
694
+ * + bundle txs accurately instead of picking a single "primary leg"
695
+ * (which made `Swapped 987.60 MANIFEST` look like +$987 of value when
696
+ * the user actually paid 1 USDC for it).
697
+ */
698
+ interface TransactionLeg {
699
+ /** Full Sui coin type string (e.g. `0x...usdc::USDC`). */
700
+ coinType: string;
701
+ /** Display symbol (USDC, SUI, GOLD, MANIFEST, …) from the token registry. */
702
+ asset: string;
703
+ /** On-chain decimals for this coin (used to format `amount`). */
704
+ decimals: number;
705
+ /** Token quantity as a positive number (e.g. 987.60). */
706
+ amount: number;
707
+ /** Signed raw bigint as a string (preserves sign + precision). */
708
+ rawAmount: string;
709
+ /** `'out'` if the user spent this coin, `'in'` if they received it. */
710
+ direction: 'in' | 'out';
711
+ }
712
+ interface TransactionRecord {
713
+ digest: string;
714
+ /** Coarse bucket — `'send' | 'lending' | 'swap' | 'transaction'`. STABLE. */
715
+ action: string;
716
+ /**
717
+ * Finer-grained display label derived from the Move-call function
718
+ * name (e.g. `'deposit'`, `'withdraw'`, `'payment_link'`,
719
+ * `'on-chain'`). Optional — frontends should fall back to `action`
720
+ * when missing. Never used by ACI filters.
721
+ */
722
+ label?: string;
723
+ /**
724
+ * All non-zero user balance legs for this transaction. Single-write
725
+ * txs have `legs.length === 1`; swaps have `2` (one `out`, one
726
+ * `in`); bundles have `> 2`. Order is RPC order — not sorted by
727
+ * size or USD value (audric's activity route prices + sorts).
728
+ *
729
+ * @since SDK v1.27.2 — was missing from earlier shapes; older
730
+ * consumers can keep using `amount` / `asset` / `direction` (which
731
+ * still resolve to the largest absolute leg).
732
+ */
733
+ legs: TransactionLeg[];
734
+ /**
735
+ * Largest-absolute-leg amount, kept for back-compat with consumers
736
+ * that pre-date `legs[]`. New code should iterate `legs` instead.
737
+ */
738
+ amount?: number;
739
+ /** @see {@link amount} — back-compat alias for `legs[primary].asset`. */
740
+ asset?: string;
741
+ recipient?: string;
742
+ /**
743
+ * Direction of the user's principal (non-gas) balance movement on
744
+ * this tx — `'out'` if they spent, `'in'` if they received.
745
+ * Computed from on-chain balance changes (NOT from `label`), so the
746
+ * card can render the correct sign even for opaque actions like
747
+ * `swap`/`router`. Undefined when no user balance change is
748
+ * detectable (e.g. pure read-only or admin txs).
749
+ *
750
+ * @see {@link amount} — back-compat alias for `legs[primary].direction`.
751
+ */
752
+ direction?: 'in' | 'out';
753
+ timestamp: number;
754
+ gasCost?: number;
755
+ }
756
+ interface SwapResult {
757
+ success: boolean;
758
+ tx: string;
759
+ fromToken: string;
760
+ toToken: string;
761
+ fromAmount: number;
762
+ toAmount: number;
763
+ priceImpact: number;
764
+ route: string;
765
+ gasCost: number;
766
+ }
767
+ interface SwapQuoteResult {
768
+ fromToken: string;
769
+ toToken: string;
770
+ fromAmount: number;
771
+ toAmount: number;
772
+ priceImpact: number;
773
+ route: string;
774
+ /**
775
+ * [SPEC 20.2 / D-1 (a)] Structured Cetus route captured at quote time.
776
+ * Threaded through `pending_action.cetusRoute` so the prepare-route can
777
+ * skip the ~400-500ms `findSwapRoute()` re-discovery, and so the
778
+ * post-write resume system prompt can ground LLM narration against the
779
+ * canonical route (closing S19-F2). Optional for backward compat with
780
+ * pre-SPEC-20.2 callers (CLI, server-only direct calls).
781
+ */
782
+ serializedRoute?: SerializedCetusRoute;
783
+ }
784
+ interface PayOptions {
785
+ url: string;
786
+ method?: string;
787
+ body?: string;
788
+ headers?: Record<string, string>;
789
+ maxPrice?: number;
790
+ }
791
+ interface PayResult {
792
+ status: number;
793
+ body: unknown;
794
+ paid: boolean;
795
+ /**
796
+ * Which payment dialect settled the call. `'x402'` = the sign-then-settle
797
+ * x402 `sui-exact` scheme (client signs, gateway settles); `'legacy'` = the
798
+ * pre-x402 MPP digest dialect (client broadcasts, retries with the digest).
799
+ * Undefined when nothing was paid (free/cached endpoint). See
800
+ * SUIMPP_X402_SCHEME.md.
801
+ */
802
+ dialect?: 'x402' | 'legacy';
803
+ cost?: number;
804
+ /**
805
+ * SUI gas cost actually paid on chain. Zero for gasless payments —
806
+ * which means an MPP payment hit the protocol's gasless allowlist
807
+ * (USDC / USDsui / USDY / FdUSD / AUSD / BUCK / USDB / SUI_USDE) and
808
+ * was accepted with `gasPrice=0, gasBudget=0, gasPayment=[]`. See
809
+ * https://docs.sui.io/develop/transaction-payment/gasless-stablecoin-transfers
810
+ */
811
+ gasCostSui?: number;
812
+ receipt?: {
813
+ reference: string;
814
+ timestamp: string;
815
+ };
816
+ }
817
+
818
+ declare function payWithMpp(args: {
819
+ signer: TransactionSigner;
820
+ client: SuiGrpcClient;
821
+ options: PayOptions;
822
+ }): Promise<PayResult>;
823
+
824
+ type SuiTransactionEffects = SuiClientTypes.TransactionEffects;
825
+ type BuildClient = NonNullable<Parameters<Transaction['build']>[0]>['client'];
826
+ declare function executeTx(client: SuiCoreClient, signer: TransactionSigner, buildTx: () => Promise<Transaction> | Transaction, options?: {
827
+ buildClient?: BuildClient;
828
+ }): Promise<{
829
+ digest: string;
830
+ gasCostSui: number;
831
+ effects: SuiTransactionEffects | undefined;
832
+ }>;
833
+
834
+ 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';
835
+ interface T2000ErrorData {
836
+ reason?: string;
837
+ [key: string]: unknown;
838
+ }
839
+ declare class T2000Error extends Error {
840
+ readonly code: T2000ErrorCode;
841
+ readonly data?: T2000ErrorData;
842
+ readonly retryable: boolean;
843
+ constructor(code: T2000ErrorCode, message: string, data?: T2000ErrorData, retryable?: boolean);
844
+ toJSON(): {
845
+ retryable: boolean;
846
+ data?: T2000ErrorData | undefined;
847
+ error: T2000ErrorCode;
848
+ message: string;
849
+ };
850
+ }
851
+ declare function mapWalletError(error: unknown): T2000Error;
852
+ declare function mapMoveAbortCode(code: number): string;
853
+
854
+ declare const MIST_PER_SUI = 1000000000n;
855
+ declare const SUI_DECIMALS = 9;
856
+ declare const USDC_DECIMALS = 6;
857
+ declare const CLOCK_ID = "0x6";
858
+ declare const SUPPORTED_ASSETS: {
859
+ readonly USDC: {
860
+ readonly type: "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
861
+ readonly decimals: 6;
862
+ readonly symbol: "USDC";
863
+ readonly displayName: "USDC";
864
+ };
865
+ readonly USDT: {
866
+ readonly type: "0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT";
867
+ readonly decimals: 6;
868
+ readonly symbol: "USDT";
869
+ readonly displayName: "suiUSDT";
870
+ };
871
+ readonly USDe: {
872
+ readonly type: "0x41d587e5336f1c86cad50d38a7136db99333bb9bda91cea4ba69115defeb1402::sui_usde::SUI_USDE";
873
+ readonly decimals: 6;
874
+ readonly symbol: "USDe";
875
+ readonly displayName: "suiUSDe";
876
+ };
877
+ readonly USDsui: {
878
+ readonly type: "0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI";
879
+ readonly decimals: 6;
880
+ readonly symbol: "USDsui";
881
+ readonly displayName: "USDsui";
882
+ };
883
+ readonly SUI: {
884
+ readonly type: "0x2::sui::SUI";
885
+ readonly decimals: 9;
886
+ readonly symbol: "SUI";
887
+ readonly displayName: "SUI";
888
+ };
889
+ readonly WAL: {
890
+ readonly type: "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL";
891
+ readonly decimals: 9;
892
+ readonly symbol: "WAL";
893
+ readonly displayName: "WAL";
894
+ };
895
+ readonly ETH: {
896
+ readonly type: "0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH";
897
+ readonly decimals: 8;
898
+ readonly symbol: "ETH";
899
+ readonly displayName: "suiETH";
900
+ };
901
+ readonly NAVX: {
902
+ readonly type: "0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX";
903
+ readonly decimals: 9;
904
+ readonly symbol: "NAVX";
905
+ readonly displayName: "NAVX";
906
+ };
907
+ readonly GOLD: {
908
+ readonly type: "0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM";
909
+ readonly decimals: 6;
910
+ readonly symbol: "GOLD";
911
+ readonly displayName: "XAUM";
912
+ };
913
+ };
914
+ type SupportedAsset = keyof typeof SUPPORTED_ASSETS;
915
+ type StableAsset = 'USDC' | 'USDsui';
916
+ declare const STABLE_ASSETS: readonly StableAsset[];
917
+ declare const OPERATION_ASSETS: {
918
+ readonly send: readonly ["USDC", "USDsui", "SUI"];
919
+ readonly swap: "*";
920
+ };
921
+ type Operation = keyof typeof OPERATION_ASSETS;
922
+ declare function isAllowedAsset(op: Operation, asset: string): boolean;
923
+ /**
924
+ * Throws if the asset is not permitted for the given operation.
925
+ *
926
+ * [v4.0 Phase A Day 2] Pre-v4 this allowed `undefined` as a silent default
927
+ * to USDC. Removed because every write path now requires explicit asset
928
+ * (see `T2000.send` + `buildSendTx` + `composeTx.send_transfer`). The
929
+ * `undefined → no-op` branch is kept defensively; the `send_transfer` flow
930
+ * validates non-undefined.
931
+ */
932
+ declare function assertAllowedAsset(op: Operation, asset: string | undefined): void;
933
+ /**
934
+ * [v4.0 Phase A Day 2] Narrow type alias for assets sendable through the
935
+ * Agent Wallet. Matches `OPERATION_ASSETS.send` exactly. Exported so the
936
+ * CLI / SDK / composeTx can share one type without re-declaring it.
937
+ */
938
+ type SendableAsset = 'USDC' | 'USDsui' | 'SUI';
939
+ declare const SENDABLE_ASSETS: readonly SendableAsset[];
940
+ /**
941
+ * [v4.0 Phase A Day 2] Coin types for the two gasless-allowlisted stables.
942
+ * Used by `wallet/send.ts` + `composeTx.send_transfer` to construct the
943
+ * `0x2::balance::send_funds` Move call's `typeArguments`. SUI is excluded
944
+ * because SUI transfers are NOT gasless (gas-native, uses `tx.gas` split +
945
+ * `transferObjects` per the existing path).
946
+ */
947
+ declare const GASLESS_STABLE_TYPES: Record<'USDC' | 'USDsui', string>;
948
+ declare const T2000_OVERLAY_FEE_WALLET: string;
949
+ declare const DEFAULT_NETWORK: "mainnet";
950
+ declare const DEFAULT_GRPC_URL = "https://fullnode.mainnet.sui.io:443";
951
+ declare const GASLESS_MIN_STABLE_AMOUNT = 0.01;
952
+ declare const CETUS_USDC_SUI_POOL = "0x51e883ba7c0b566a26cbc8a94cd33eb0abd418a77cc1e60ad22fd9b1f29cd2ab";
953
+ declare const GAS_RESERVE_MIN = 0.05;
954
+
955
+ /**
956
+ * Shared transaction classifier.
957
+ *
958
+ * Consumed by both the SDK's `parseTxRecord` (production agent path) and
959
+ * the engine's `transaction_history` tool (cold-start RPC path). Keeping
960
+ * a single source of truth here prevents the two paths from drifting —
961
+ * see v1.5.3 regression where the SDK path was emitting `action:
962
+ * 'transaction'` (rendered as "On-chain") while the engine path was
963
+ * already producing fine-grained labels.
964
+ */
965
+ /**
966
+ * Coarse action bucket — one of `'send' | 'lending' | 'swap' |
967
+ * 'transaction'`. Used by the ACI `action` filter on the
968
+ * `transaction_history` tool. STABLE: downstream queries depend on
969
+ * exactly these values.
970
+ *
971
+ * Order matters: more specific buckets first. Lending patterns precede
972
+ * swap patterns so a NAVI `::swap` helper (if one ever existed) would
973
+ * still bucket as lending.
974
+ */
975
+ declare const KNOWN_TARGETS: readonly [RegExp, string][];
976
+ /**
977
+ * Finer-grained display labels — derived from MoveCall function names.
978
+ * The card renders `label ?? action`, so when this map matches we get
979
+ * "Deposit" / "Withdraw" / "Borrow" / "Repay" / "Payment link" instead
980
+ * of the generic "Lending" or "Transaction".
981
+ *
982
+ * Order matters: more specific patterns first. Each entry is
983
+ * (regex, label) where the regex is matched against the
984
+ * fully-qualified MoveCall target `pkg::module::function`.
985
+ */
986
+ declare const LABEL_PATTERNS: readonly [RegExp, string][];
987
+ interface ClassifyBalanceChange {
988
+ owner: {
989
+ AddressOwner?: string;
990
+ } | string;
991
+ coinType: string;
992
+ amount: string;
993
+ }
994
+ declare function classifyAction(targets: string[], commandTypes: string[]): string;
995
+ /**
996
+ * Last-resort fallback when neither `LABEL_PATTERNS` nor the action
997
+ * bucket produces something useful.
998
+ *
999
+ * Returns the first MoveCall's *module* name (e.g. "navi", "spam") so
1000
+ * the card shows something better than the literal word "transaction".
1001
+ * When no MoveCall exists, returns 'on-chain'.
1002
+ *
1003
+ * Note: callers should prefer `classifyLabel` which now layers
1004
+ * pattern-match → coarse action → module name (see commentary there).
1005
+ */
1006
+ declare function fallbackLabel(targets: string[]): string;
1007
+ /**
1008
+ * Three-tier label resolution for the transaction history card:
1009
+ * 1. Specific keyword match in `LABEL_PATTERNS` ("deposit",
1010
+ * "payment_link", …).
1011
+ * 2. Coarse action bucket from `classifyAction` ("swap", "send",
1012
+ * "lending") — prevents leaking opaque internal module names like
1013
+ * "router" (Cetus aggregator) or "cross_swap" (third-party DEX
1014
+ * aggregators) for txs that we already classified as a swap.
1015
+ * 3. Module name from the first MoveCall (`fallbackLabel`) — only
1016
+ * used when the action bucket itself is the generic "transaction".
1017
+ *
1018
+ * Pre-v0.46.2 we skipped tier 2, so swaps showed labels like "router",
1019
+ * "cross_swap", "scallop_router", etc. instead of the clean "swap".
1020
+ */
1021
+ declare function classifyLabel(targets: string[], commandTypes: string[]): string;
1022
+ /**
1023
+ * Balance-direction tiebreaker for ambiguous lending calls.
1024
+ *
1025
+ * Many lending modules expose generic entry points (NAVI's bundled
1026
+ * flash actions, `lending_core::*::entry_*`, etc.) that don't carry
1027
+ * a `deposit`/`withdraw`/`borrow`/`repay` keyword in the function
1028
+ * name. When `classifyLabel` falls back to a bare module name like
1029
+ * `"lending"` for a known lending tx, infer direction from the user's
1030
+ * non-SUI balance change:
1031
+ * - net outflow of the supplied asset → deposit (also covers repay,
1032
+ * but repay-without-keyword is essentially never emitted).
1033
+ * - net inflow of the supplied asset → withdraw (also covers borrow).
1034
+ * SUI is excluded so gas-only transactions don't get mislabeled.
1035
+ *
1036
+ * If `LABEL_PATTERNS` matched a specific keyword, the existing label is
1037
+ * returned unchanged.
1038
+ */
1039
+ declare function refineLendingLabel(currentAction: string, currentLabel: string, moveCallTargets: string[], changes: ClassifyBalanceChange[], address: string): string;
1040
+ interface ClassifyResult {
1041
+ action: string;
1042
+ label: string;
1043
+ }
1044
+ declare function classifyTransaction(moveCallTargets: string[], commandTypes: string[], balanceChanges: ClassifyBalanceChange[], address: string): ClassifyResult;
1045
+ /**
1046
+ * Direction of the user's net non-gas movement for this transaction.
1047
+ *
1048
+ * - `'out'` — the user spent the asset (sends, deposits, repays,
1049
+ * swap-in, payment-link payouts).
1050
+ * - `'in'` — the user received the asset (withdraws, borrows,
1051
+ * swap-out, claims, deposits credited from another wallet).
1052
+ *
1053
+ * Used by the `TransactionHistoryCard` to choose the `+`/`−` sign and
1054
+ * color. Direction is computed from the actual on-chain balance change
1055
+ * — never from the textual label — so opaque action types (`'router'`,
1056
+ * `'cross_swap'`, …) still render the correct sign.
1057
+ */
1058
+ type TxDirection = 'in' | 'out';
1059
+ interface ExtractedTransfer {
1060
+ amount?: number;
1061
+ asset?: string;
1062
+ recipient?: string;
1063
+ direction?: TxDirection;
1064
+ }
1065
+ /**
1066
+ * Extracts the principal amount/asset/direction for a transaction
1067
+ * from its `balanceChanges`.
1068
+ *
1069
+ * Algorithm:
1070
+ * 1. Restrict to the user's *own* balance changes.
1071
+ * 2. Prefer non-SUI changes (gas-only SUI deltas are noise).
1072
+ * 3. Pick the change with the largest absolute value — the "principal".
1073
+ * 4. If no non-SUI change exists, fall back to the largest SUI change
1074
+ * so pure-SUI transfers (stake/unstake/native send) still render.
1075
+ * 5. Direction follows the sign of the principal.
1076
+ * 6. Recipient is set only on outflows, by finding a matching inflow
1077
+ * on a *non-user* address with the same coinType.
1078
+ *
1079
+ * Pre-v0.46.2 this function only inspected outflows, so withdraws,
1080
+ * borrows, claims, swap-receives and payment-link receives all
1081
+ * rendered with no amount on the rich card (and with a wrong sign,
1082
+ * because the card guessed direction from the label string).
1083
+ */
1084
+ declare function extractTransferDetails(changes: ClassifyBalanceChange[] | undefined, sender: string): ExtractedTransfer;
1085
+ /**
1086
+ * Extract every non-zero user balance leg for a transaction — not
1087
+ * just the largest one. Order is RPC order; callers responsible for
1088
+ * any sorting (e.g. audric sorts by USD value once it's priced).
1089
+ *
1090
+ * Sui collapses balance changes by coin type, so a 3-step bundle
1091
+ * touching USDC three times surfaces as ONE leg of net USDC delta.
1092
+ * Distinguishing per-step legs would require parsing the PTB's
1093
+ * commands; this helper deliberately stops at the balance-change
1094
+ * granularity because that's what's reliably available across all
1095
+ * Sui RPC versions.
1096
+ *
1097
+ * Pre-v1.27.2 the only public API was `extractTransferDetails`,
1098
+ * which returned a single "primary" leg and made swap rows
1099
+ * unrenderable (showed `Swapped 987.60 MANIFEST` because MANIFEST
1100
+ * was the largest raw delta even though USDC was the value side).
1101
+ */
1102
+ declare function extractAllUserLegs(changes: ClassifyBalanceChange[] | undefined, sender: string): TransactionLeg[];
1103
+
1104
+ declare function queryHistory(address: string, limit?: number): Promise<TransactionRecord[]>;
1105
+ declare function queryTransaction(digest: string, senderAddress: string): Promise<TransactionRecord | null>;
1106
+ /**
1107
+ * Shape of a transaction block as returned by `suix_queryTransactionBlocks`
1108
+ * with `showEffects | showInput | showBalanceChanges` enabled. Exported so
1109
+ * downstream consumers (audric dashboard `/api/history`, `/api/activity`,
1110
+ * etc.) can type their RPC calls without redeclaring the structure.
1111
+ */
1112
+ interface SuiRpcTxBlock {
1113
+ digest: string;
1114
+ timestampMs?: string;
1115
+ transaction?: unknown;
1116
+ effects?: {
1117
+ gasUsed?: {
1118
+ computationCost: string;
1119
+ storageCost: string;
1120
+ storageRebate: string;
1121
+ };
1122
+ };
1123
+ balanceChanges?: ClassifyBalanceChange[];
1124
+ }
1125
+ /**
1126
+ * Convert a single Sui RPC transaction block to a {@link TransactionRecord}
1127
+ * using the canonical (shared) classifier and balance-change extractor.
1128
+ *
1129
+ * This is the single source of truth for transaction parsing across the
1130
+ * agent-tool path AND the dashboard-API path. Use it instead of writing
1131
+ * a bespoke parser per surface.
1132
+ *
1133
+ * @param tx Raw RPC tx block (must include `effects`, `input`, `balanceChanges`).
1134
+ * @param address Wallet address whose perspective we're parsing from.
1135
+ */
1136
+ declare function parseSuiRpcTx(tx: SuiRpcTxBlock, address: string): TransactionRecord;
1137
+ /**
1138
+ * Extract the sender (signer) address from a raw RPC tx block.
1139
+ * Returns `null` if the block shape is unexpected.
1140
+ */
1141
+ declare function extractTxSender(txBlock: unknown): string | null;
1142
+ /**
1143
+ * Extract MoveCall targets (`<pkg>::<module>::<function>`) and the
1144
+ * sequence of programmable-transaction command types (e.g. `MoveCall`,
1145
+ * `TransferObjects`) from a raw RPC tx block. Tolerates both the
1146
+ * legacy `inner.transactions` field and the newer `inner.commands`
1147
+ * field.
1148
+ */
1149
+ declare function extractTxCommands(txBlock: unknown): {
1150
+ moveCallTargets: string[];
1151
+ commandTypes: string[];
1152
+ };
1153
+
1154
+ declare function mistToSui(mist: bigint): number;
1155
+ declare function suiToMist(sui: number): bigint;
1156
+ declare function usdcToRaw(amount: number): bigint;
1157
+ declare function rawToUsdc(raw: bigint): number;
1158
+ declare function stableToRaw(amount: number, decimals: number): bigint;
1159
+ declare function rawToStable(raw: bigint, decimals: number): number;
1160
+ declare function getDecimals(asset: SupportedAsset): number;
1161
+ declare function formatUsd(amount: number): string;
1162
+ declare function formatSui(amount: number): string;
1163
+ declare function formatAssetAmount(amount: number, asset: string): string;
1164
+ /**
1165
+ * Case-insensitive lookup against SUPPORTED_ASSETS keys AND display names.
1166
+ * 'usde' → 'USDe', 'suiusde' → 'USDe', 'suiusdt' → 'USDT', 'usdsui' → 'USDsui'.
1167
+ * Returns the original input if not found so downstream validation can reject it.
1168
+ */
1169
+ declare function normalizeAsset(input: string): string;
1170
+
1171
+ interface SimulationResult {
1172
+ success: boolean;
1173
+ gasEstimateSui: number;
1174
+ error?: {
1175
+ moveAbortCode?: number;
1176
+ moveModule?: string;
1177
+ reason: string;
1178
+ rawError: string;
1179
+ };
1180
+ }
1181
+ declare function simulateTransaction(client: SuiCoreClient, tx: Transaction, sender: string): Promise<SimulationResult>;
1182
+ declare function throwIfSimulationFailed(sim: SimulationResult): void;
1183
+
1184
+ type SafeguardRule = 'locked' | 'maxPerTx' | 'maxDailySend';
1185
+ interface SafeguardErrorDetails {
1186
+ attempted?: number;
1187
+ limit?: number;
1188
+ current?: number;
1189
+ }
1190
+ declare class SafeguardError extends T2000Error {
1191
+ readonly rule: SafeguardRule;
1192
+ readonly details: SafeguardErrorDetails;
1193
+ constructor(rule: SafeguardRule, details: SafeguardErrorDetails, message?: string);
1194
+ toJSON(): {
1195
+ error: "SAFEGUARD_BLOCKED";
1196
+ message: string;
1197
+ retryable: boolean;
1198
+ data: {
1199
+ attempted?: number;
1200
+ limit?: number;
1201
+ current?: number;
1202
+ rule: SafeguardRule;
1203
+ };
1204
+ };
1205
+ }
1206
+
1207
+ interface SafeguardConfig {
1208
+ locked: boolean;
1209
+ maxPerTx: number;
1210
+ maxDailySend: number;
1211
+ dailyUsed: number;
1212
+ dailyResetDate: string;
1213
+ maxLeverage?: number;
1214
+ maxPositionSize?: number;
1215
+ }
1216
+ interface TxMetadata {
1217
+ operation: 'send' | 'pay';
1218
+ amount?: number;
1219
+ }
1220
+ declare const DEFAULT_SAFEGUARD_CONFIG: SafeguardConfig;
1221
+
1222
+ export { USDT_TYPE as $, type T2000ErrorCode as A, type BalanceResponse as B, CLOCK_ID as C, DEFAULT_NETWORK as D, ETH_TYPE as E, type T2000ErrorData as F, GAS_RESERVE_MIN as G, T2000_OVERLAY_FEE_WALLET as H, IKA_TYPE as I, TOKEN_MAP as J, KNOWN_TARGETS as K, LABEL_PATTERNS as L, MANIFEST_TYPE as M, NAVX_TYPE as N, OVERLAY_FEE_RATE as O, type PayOptions as P, type TransactionLeg as Q, type TransactionRecord as R, STABLE_ASSETS as S, T2000Error as T, type TransactionSigner as U, type TxDirection as V, type TxMetadata as W, USDC_DECIMALS as X, USDC_TYPE as Y, USDE_TYPE as Z, USDSUI_TYPE as _, COIN_REGISTRY as a, isInRegistry as a$, WAL_TYPE as a0, WBTC_TYPE as a1, type ZkLoginProof as a2, ZkLoginSigner as a3, buildSwapTx as a4, classifyAction as a5, classifyLabel as a6, classifyTransaction as a7, executeTx as a8, extractAllUserLegs as a9, type SwapResult as aA, type SwapQuoteResult as aB, type PaymentRequest as aC, type SuiCoreClient as aD, type SendableAsset as aE, type SponsoredCoinMergeCache as aF, CETUS_USDC_SUI_POOL as aG, type CoinPage as aH, DEFAULT_GRPC_URL as aI, GASLESS_MIN_STABLE_AMOUNT as aJ, GASLESS_STABLE_TYPES as aK, OPERATION_ASSETS as aL, type Operation as aM, SENDABLE_ASSETS as aN, type SelectAndSplitResult as aO, type SerializedCetusRoute as aP, type SerializedCetusRoutePath as aQ, type SerializedRouterDataV3 as aR, addSwapToTx as aS, assertAllowedAsset as aT, deserializeCetusRoute as aU, fetchAllCoins as aV, getCoinMeta as aW, getSuiClient as aX, getSuiGrpcClient as aY, isAllowedAsset as aZ, isCetusRouteFresh 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, rawToStable as ap, rawToUsdc as aq, refineLendingLabel as ar, resolveSymbol as as, resolveTokenType as at, stableToRaw as au, suiToMist as av, truncateAddress as aw, usdcToRaw as ax, validateAddress as ay, type T2000Options as az, type ClassifyBalanceChange as b, normalizeAsset as b0, normalizeCoinType as b1, queryHistory as b2, queryTransaction as b3, selectAndSplitCoin as b4, selectSuiCoin as b5, serializeCetusRoute as b6, simulateTransaction as b7, throwIfSimulationFailed as b8, verifyCetusRouteCoinMatch as b9, type ClassifyResult as c, type CoinMeta as d, DEFAULT_SAFEGUARD_CONFIG as e, type DepositInfo as f, type ExtractedTransfer as g, KeypairSigner as h, LOFI_TYPE as i, MIST_PER_SUI as j, type OverlayFeeConfig as k, type PayResult as l, SUI_DECIMALS as m, SUI_TYPE as n, SUPPORTED_ASSETS as o, type SafeguardConfig as p, SafeguardError as q, type SafeguardErrorDetails as r, type SafeguardRule as s, type SendResult as t, type SimulationResult as u, type StableAsset as v, type SuiHolding as w, type SuiRpcTxBlock as x, type SupportedAsset as y, type SwapRouteResult as z };