@t2000/sdk 0.10.4 → 0.11.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.
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ import { T as T2000Options, S as SendResult, B as BalanceResponse, a as Transact
5
5
  export { A as AdapterCapability, l as AdapterPositions, m as AdapterTxResult, n as AssetRates, C as CetusAdapter, o as GasReserve, p as HealthInfo, N as NaviAdapter, q as PositionEntry, r as ProtocolDescriptor, s as ProtocolRegistry, t as RebalanceStep, u as SentinelVerdict, v as SuilendAdapter, w as SwapQuote, x as allDescriptors, y as cetusDescriptor, z as getSentinelInfo, I as listSentinels, J as naviDescriptor, K as requestAttack, O as sentinelAttack, Q as sentinelDescriptor, U as settleAttack, V as submitPrompt, X as suilendDescriptor } from './index-UOQejD-B.cjs';
6
6
  import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
7
7
 
8
- type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'UNKNOWN';
8
+ type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'UNKNOWN';
9
9
  interface T2000ErrorData {
10
10
  reason?: string;
11
11
  [key: string]: unknown;
@@ -25,6 +25,37 @@ declare class T2000Error extends Error {
25
25
  declare function mapWalletError(error: unknown): T2000Error;
26
26
  declare function mapMoveAbortCode(code: number): string;
27
27
 
28
+ interface SafeguardConfig {
29
+ locked: boolean;
30
+ maxPerTx: number;
31
+ maxDailySend: number;
32
+ dailyUsed: number;
33
+ dailyResetDate: string;
34
+ }
35
+ interface TxMetadata {
36
+ operation: 'send' | 'save' | 'withdraw' | 'borrow' | 'repay' | 'exchange' | 'rebalance' | 'pay' | 'sentinel';
37
+ amount?: number;
38
+ }
39
+ declare const OUTBOUND_OPS: Set<"save" | "borrow" | "withdraw" | "repay" | "sentinel" | "send" | "exchange" | "rebalance" | "pay">;
40
+ declare const DEFAULT_SAFEGUARD_CONFIG: SafeguardConfig;
41
+
42
+ declare class SafeguardEnforcer {
43
+ private config;
44
+ private readonly configPath;
45
+ constructor(configDir?: string);
46
+ load(): void;
47
+ assertNotLocked(): void;
48
+ check(metadata: TxMetadata): void;
49
+ recordUsage(amount: number): void;
50
+ lock(): void;
51
+ unlock(): void;
52
+ set(key: string, value: unknown): void;
53
+ getConfig(): SafeguardConfig;
54
+ isConfigured(): boolean;
55
+ private resetDailyIfNewDay;
56
+ private save;
57
+ }
58
+
28
59
  interface T2000Events {
29
60
  balanceChange: (event: {
30
61
  asset: string;
@@ -65,6 +96,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
65
96
  private readonly client;
66
97
  private readonly _address;
67
98
  private readonly registry;
99
+ readonly enforcer: SafeguardEnforcer;
68
100
  private constructor();
69
101
  private static createDefaultRegistry;
70
102
  static create(options?: T2000Options): Promise<T2000>;
@@ -278,6 +310,29 @@ declare function getPoolPrice(client: SuiJsonRpcClient): Promise<number>;
278
310
 
279
311
  declare function getRates(client: SuiJsonRpcClient): Promise<RatesResult>;
280
312
 
313
+ type SafeguardRule = 'locked' | 'maxPerTx' | 'maxDailySend';
314
+ interface SafeguardErrorDetails {
315
+ attempted?: number;
316
+ limit?: number;
317
+ current?: number;
318
+ }
319
+ declare class SafeguardError extends T2000Error {
320
+ readonly rule: SafeguardRule;
321
+ readonly details: SafeguardErrorDetails;
322
+ constructor(rule: SafeguardRule, details: SafeguardErrorDetails, message?: string);
323
+ toJSON(): {
324
+ error: "SAFEGUARD_BLOCKED";
325
+ message: string;
326
+ retryable: boolean;
327
+ data: {
328
+ attempted?: number;
329
+ limit?: number;
330
+ current?: number;
331
+ rule: SafeguardRule;
332
+ };
333
+ };
334
+ }
335
+
281
336
  interface GasExecutionResult {
282
337
  digest: string;
283
338
  effects: unknown;
@@ -291,7 +346,10 @@ interface GasExecutionResult {
291
346
  * 3. Gas Station sponsored (fallback)
292
347
  * 4. Fail with INSUFFICIENT_GAS
293
348
  */
294
- declare function executeWithGas(client: SuiJsonRpcClient, keypair: Ed25519Keypair, buildTx: () => Transaction | Promise<Transaction>): Promise<GasExecutionResult>;
349
+ declare function executeWithGas(client: SuiJsonRpcClient, keypair: Ed25519Keypair, buildTx: () => Transaction | Promise<Transaction>, options?: {
350
+ metadata?: TxMetadata;
351
+ enforcer?: SafeguardEnforcer;
352
+ }): Promise<GasExecutionResult>;
295
353
 
296
354
  interface AutoTopUpResult {
297
355
  success: boolean;
@@ -317,4 +375,4 @@ interface GasStatusResponse {
317
375
  }
318
376
  declare function getGasStatus(address?: string): Promise<GasStatusResponse>;
319
377
 
320
- export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, DEFAULT_NETWORK, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
378
+ export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { T as T2000Options, S as SendResult, B as BalanceResponse, a as Transact
5
5
  export { A as AdapterCapability, l as AdapterPositions, m as AdapterTxResult, n as AssetRates, C as CetusAdapter, o as GasReserve, p as HealthInfo, N as NaviAdapter, q as PositionEntry, r as ProtocolDescriptor, s as ProtocolRegistry, t as RebalanceStep, u as SentinelVerdict, v as SuilendAdapter, w as SwapQuote, x as allDescriptors, y as cetusDescriptor, z as getSentinelInfo, I as listSentinels, J as naviDescriptor, K as requestAttack, O as sentinelAttack, Q as sentinelDescriptor, U as settleAttack, V as submitPrompt, X as suilendDescriptor } from './index-UOQejD-B.js';
6
6
  import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
7
7
 
8
- type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'UNKNOWN';
8
+ type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'UNKNOWN';
9
9
  interface T2000ErrorData {
10
10
  reason?: string;
11
11
  [key: string]: unknown;
@@ -25,6 +25,37 @@ declare class T2000Error extends Error {
25
25
  declare function mapWalletError(error: unknown): T2000Error;
26
26
  declare function mapMoveAbortCode(code: number): string;
27
27
 
28
+ interface SafeguardConfig {
29
+ locked: boolean;
30
+ maxPerTx: number;
31
+ maxDailySend: number;
32
+ dailyUsed: number;
33
+ dailyResetDate: string;
34
+ }
35
+ interface TxMetadata {
36
+ operation: 'send' | 'save' | 'withdraw' | 'borrow' | 'repay' | 'exchange' | 'rebalance' | 'pay' | 'sentinel';
37
+ amount?: number;
38
+ }
39
+ declare const OUTBOUND_OPS: Set<"save" | "borrow" | "withdraw" | "repay" | "sentinel" | "send" | "exchange" | "rebalance" | "pay">;
40
+ declare const DEFAULT_SAFEGUARD_CONFIG: SafeguardConfig;
41
+
42
+ declare class SafeguardEnforcer {
43
+ private config;
44
+ private readonly configPath;
45
+ constructor(configDir?: string);
46
+ load(): void;
47
+ assertNotLocked(): void;
48
+ check(metadata: TxMetadata): void;
49
+ recordUsage(amount: number): void;
50
+ lock(): void;
51
+ unlock(): void;
52
+ set(key: string, value: unknown): void;
53
+ getConfig(): SafeguardConfig;
54
+ isConfigured(): boolean;
55
+ private resetDailyIfNewDay;
56
+ private save;
57
+ }
58
+
28
59
  interface T2000Events {
29
60
  balanceChange: (event: {
30
61
  asset: string;
@@ -65,6 +96,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
65
96
  private readonly client;
66
97
  private readonly _address;
67
98
  private readonly registry;
99
+ readonly enforcer: SafeguardEnforcer;
68
100
  private constructor();
69
101
  private static createDefaultRegistry;
70
102
  static create(options?: T2000Options): Promise<T2000>;
@@ -278,6 +310,29 @@ declare function getPoolPrice(client: SuiJsonRpcClient): Promise<number>;
278
310
 
279
311
  declare function getRates(client: SuiJsonRpcClient): Promise<RatesResult>;
280
312
 
313
+ type SafeguardRule = 'locked' | 'maxPerTx' | 'maxDailySend';
314
+ interface SafeguardErrorDetails {
315
+ attempted?: number;
316
+ limit?: number;
317
+ current?: number;
318
+ }
319
+ declare class SafeguardError extends T2000Error {
320
+ readonly rule: SafeguardRule;
321
+ readonly details: SafeguardErrorDetails;
322
+ constructor(rule: SafeguardRule, details: SafeguardErrorDetails, message?: string);
323
+ toJSON(): {
324
+ error: "SAFEGUARD_BLOCKED";
325
+ message: string;
326
+ retryable: boolean;
327
+ data: {
328
+ attempted?: number;
329
+ limit?: number;
330
+ current?: number;
331
+ rule: SafeguardRule;
332
+ };
333
+ };
334
+ }
335
+
281
336
  interface GasExecutionResult {
282
337
  digest: string;
283
338
  effects: unknown;
@@ -291,7 +346,10 @@ interface GasExecutionResult {
291
346
  * 3. Gas Station sponsored (fallback)
292
347
  * 4. Fail with INSUFFICIENT_GAS
293
348
  */
294
- declare function executeWithGas(client: SuiJsonRpcClient, keypair: Ed25519Keypair, buildTx: () => Transaction | Promise<Transaction>): Promise<GasExecutionResult>;
349
+ declare function executeWithGas(client: SuiJsonRpcClient, keypair: Ed25519Keypair, buildTx: () => Transaction | Promise<Transaction>, options?: {
350
+ metadata?: TxMetadata;
351
+ enforcer?: SafeguardEnforcer;
352
+ }): Promise<GasExecutionResult>;
295
353
 
296
354
  interface AutoTopUpResult {
297
355
  success: boolean;
@@ -317,4 +375,4 @@ interface GasStatusResponse {
317
375
  }
318
376
  declare function getGasStatus(address?: string): Promise<GasStatusResponse>;
319
377
 
320
- export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, DEFAULT_NETWORK, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
378
+ export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
package/dist/index.js CHANGED
@@ -6,10 +6,11 @@ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
6
6
  import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
7
7
  import { createHash, randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
8
8
  import { access, mkdir, writeFile, readFile } from 'fs/promises';
9
- import { dirname, resolve } from 'path';
9
+ import { join, dirname, resolve } from 'path';
10
10
  import { homedir } from 'os';
11
11
  import { bcs } from '@mysten/sui/bcs';
12
12
  import { AggregatorClient, Env } from '@cetusprotocol/aggregator-sdk';
13
+ import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
13
14
 
14
15
  // src/t2000.ts
15
16
 
@@ -2494,7 +2495,10 @@ async function trySponsored(client, keypair, tx) {
2494
2495
  gasCostSui: gasCost
2495
2496
  };
2496
2497
  }
2497
- async function executeWithGas(client, keypair, buildTx) {
2498
+ async function executeWithGas(client, keypair, buildTx, options) {
2499
+ if (options?.enforcer && options?.metadata) {
2500
+ options.enforcer.check(options.metadata);
2501
+ }
2498
2502
  const errors = [];
2499
2503
  try {
2500
2504
  const tx = await buildTx();
@@ -2531,18 +2535,177 @@ async function executeWithGas(client, keypair, buildTx) {
2531
2535
  );
2532
2536
  }
2533
2537
 
2534
- // src/t2000.ts
2538
+ // src/safeguards/types.ts
2539
+ var OUTBOUND_OPS = /* @__PURE__ */ new Set([
2540
+ "send",
2541
+ "pay",
2542
+ "sentinel"
2543
+ ]);
2544
+ var DEFAULT_SAFEGUARD_CONFIG = {
2545
+ locked: false,
2546
+ maxPerTx: 0,
2547
+ maxDailySend: 0,
2548
+ dailyUsed: 0,
2549
+ dailyResetDate: ""
2550
+ };
2551
+
2552
+ // src/safeguards/errors.ts
2553
+ var SafeguardError = class extends T2000Error {
2554
+ rule;
2555
+ details;
2556
+ constructor(rule, details, message) {
2557
+ const msg = message ?? buildMessage(rule, details);
2558
+ super("SAFEGUARD_BLOCKED", msg, { rule, ...details });
2559
+ this.name = "SafeguardError";
2560
+ this.rule = rule;
2561
+ this.details = details;
2562
+ }
2563
+ toJSON() {
2564
+ return {
2565
+ error: "SAFEGUARD_BLOCKED",
2566
+ message: this.message,
2567
+ retryable: this.retryable,
2568
+ data: { rule: this.rule, ...this.details }
2569
+ };
2570
+ }
2571
+ };
2572
+ function buildMessage(rule, details) {
2573
+ switch (rule) {
2574
+ case "locked":
2575
+ return "Agent is locked. All operations are frozen.";
2576
+ case "maxPerTx":
2577
+ return `Amount $${(details.attempted ?? 0).toFixed(2)} exceeds per-transaction limit ($${(details.limit ?? 0).toFixed(2)})`;
2578
+ case "maxDailySend":
2579
+ return `Daily send limit reached ($${(details.current ?? 0).toFixed(2)}/$${(details.limit ?? 0).toFixed(2)} used today)`;
2580
+ }
2581
+ }
2582
+
2583
+ // src/safeguards/enforcer.ts
2584
+ var SafeguardEnforcer = class {
2585
+ config;
2586
+ configPath;
2587
+ constructor(configDir) {
2588
+ this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
2589
+ this.configPath = configDir ? join(configDir, "config.json") : null;
2590
+ }
2591
+ load() {
2592
+ if (!this.configPath) return;
2593
+ try {
2594
+ const raw = JSON.parse(readFileSync(this.configPath, "utf-8"));
2595
+ this.config = {
2596
+ ...DEFAULT_SAFEGUARD_CONFIG,
2597
+ locked: raw.locked ?? false,
2598
+ maxPerTx: raw.maxPerTx ?? 0,
2599
+ maxDailySend: raw.maxDailySend ?? 0,
2600
+ dailyUsed: raw.dailyUsed ?? 0,
2601
+ dailyResetDate: raw.dailyResetDate ?? ""
2602
+ };
2603
+ } catch {
2604
+ this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
2605
+ }
2606
+ }
2607
+ assertNotLocked() {
2608
+ if (this.config.locked) {
2609
+ throw new SafeguardError("locked", {});
2610
+ }
2611
+ }
2612
+ check(metadata) {
2613
+ this.assertNotLocked();
2614
+ if (!OUTBOUND_OPS.has(metadata.operation)) return;
2615
+ const amount = metadata.amount ?? 0;
2616
+ if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
2617
+ throw new SafeguardError("maxPerTx", {
2618
+ attempted: amount,
2619
+ limit: this.config.maxPerTx
2620
+ });
2621
+ }
2622
+ this.resetDailyIfNewDay();
2623
+ if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
2624
+ throw new SafeguardError("maxDailySend", {
2625
+ attempted: amount,
2626
+ limit: this.config.maxDailySend,
2627
+ current: this.config.dailyUsed
2628
+ });
2629
+ }
2630
+ }
2631
+ recordUsage(amount) {
2632
+ this.resetDailyIfNewDay();
2633
+ this.config.dailyUsed += amount;
2634
+ this.save();
2635
+ }
2636
+ lock() {
2637
+ this.config.locked = true;
2638
+ this.save();
2639
+ }
2640
+ unlock() {
2641
+ this.config.locked = false;
2642
+ this.save();
2643
+ }
2644
+ set(key, value) {
2645
+ if (key === "locked" && typeof value === "boolean") {
2646
+ this.config.locked = value;
2647
+ } else if (key === "maxPerTx" && typeof value === "number") {
2648
+ this.config.maxPerTx = value;
2649
+ } else if (key === "maxDailySend" && typeof value === "number") {
2650
+ this.config.maxDailySend = value;
2651
+ }
2652
+ this.save();
2653
+ }
2654
+ getConfig() {
2655
+ this.resetDailyIfNewDay();
2656
+ return { ...this.config };
2657
+ }
2658
+ isConfigured() {
2659
+ return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
2660
+ }
2661
+ resetDailyIfNewDay() {
2662
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2663
+ if (this.config.dailyResetDate !== today) {
2664
+ this.config.dailyUsed = 0;
2665
+ this.config.dailyResetDate = today;
2666
+ this.save();
2667
+ }
2668
+ }
2669
+ save() {
2670
+ if (!this.configPath) return;
2671
+ try {
2672
+ let existing = {};
2673
+ try {
2674
+ existing = JSON.parse(readFileSync(this.configPath, "utf-8"));
2675
+ } catch {
2676
+ }
2677
+ const merged = {
2678
+ ...existing,
2679
+ locked: this.config.locked,
2680
+ maxPerTx: this.config.maxPerTx,
2681
+ maxDailySend: this.config.maxDailySend,
2682
+ dailyUsed: this.config.dailyUsed,
2683
+ dailyResetDate: this.config.dailyResetDate
2684
+ };
2685
+ const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
2686
+ if (!existsSync(dir)) {
2687
+ mkdirSync(dir, { recursive: true });
2688
+ }
2689
+ writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
2690
+ } catch {
2691
+ }
2692
+ }
2693
+ };
2694
+ var DEFAULT_CONFIG_DIR = join(homedir(), ".t2000");
2535
2695
  var T2000 = class _T2000 extends EventEmitter {
2536
2696
  keypair;
2537
2697
  client;
2538
2698
  _address;
2539
2699
  registry;
2540
- constructor(keypair, client, registry) {
2700
+ enforcer;
2701
+ constructor(keypair, client, registry, configDir) {
2541
2702
  super();
2542
2703
  this.keypair = keypair;
2543
2704
  this.client = client;
2544
2705
  this._address = getAddress(keypair);
2545
2706
  this.registry = registry ?? _T2000.createDefaultRegistry(client);
2707
+ this.enforcer = new SafeguardEnforcer(configDir);
2708
+ this.enforcer.load();
2546
2709
  }
2547
2710
  static createDefaultRegistry(client) {
2548
2711
  const registry = new ProtocolRegistry();
@@ -2566,7 +2729,7 @@ var T2000 = class _T2000 extends EventEmitter {
2566
2729
  if (secret) {
2567
2730
  await saveKey(keypair2, secret, keyPath);
2568
2731
  }
2569
- return new _T2000(keypair2, client);
2732
+ return new _T2000(keypair2, client, void 0, DEFAULT_CONFIG_DIR);
2570
2733
  }
2571
2734
  const exists = await walletExists(keyPath);
2572
2735
  if (!exists) {
@@ -2579,7 +2742,7 @@ var T2000 = class _T2000 extends EventEmitter {
2579
2742
  throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
2580
2743
  }
2581
2744
  const keypair = await loadKey(secret, keyPath);
2582
- return new _T2000(keypair, client);
2745
+ return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2583
2746
  }
2584
2747
  static fromPrivateKey(privateKey, options = {}) {
2585
2748
  const keypair = keypairFromPrivateKey(privateKey);
@@ -2591,7 +2754,7 @@ var T2000 = class _T2000 extends EventEmitter {
2591
2754
  const keypair = generateKeypair();
2592
2755
  await saveKey(keypair, secret, options.keyPath);
2593
2756
  const client = getSuiClient();
2594
- const agent = new _T2000(keypair, client);
2757
+ const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2595
2758
  const address = agent.address();
2596
2759
  let sponsored = false;
2597
2760
  if (options.sponsored !== false) {
@@ -2617,6 +2780,7 @@ var T2000 = class _T2000 extends EventEmitter {
2617
2780
  return this._address;
2618
2781
  }
2619
2782
  async send(params) {
2783
+ this.enforcer.assertNotLocked();
2620
2784
  const asset = params.asset ?? "USDC";
2621
2785
  if (!(asset in SUPPORTED_ASSETS)) {
2622
2786
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
@@ -2626,8 +2790,10 @@ var T2000 = class _T2000 extends EventEmitter {
2626
2790
  const gasResult = await executeWithGas(
2627
2791
  this.client,
2628
2792
  this.keypair,
2629
- () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
2793
+ () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset }),
2794
+ { metadata: { operation: "send", amount: sendAmount }, enforcer: this.enforcer }
2630
2795
  );
2796
+ this.enforcer.recordUsage(sendAmount);
2631
2797
  const balance = await this.balance();
2632
2798
  this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
2633
2799
  return {
@@ -2683,6 +2849,7 @@ var T2000 = class _T2000 extends EventEmitter {
2683
2849
  }
2684
2850
  // -- Savings --
2685
2851
  async save(params) {
2852
+ this.enforcer.assertNotLocked();
2686
2853
  const asset = "USDC";
2687
2854
  const bal = await queryBalance(this.client, this._address);
2688
2855
  const usdcBalance = bal.stables.USDC ?? 0;
@@ -2777,6 +2944,7 @@ var T2000 = class _T2000 extends EventEmitter {
2777
2944
  };
2778
2945
  }
2779
2946
  async withdraw(params) {
2947
+ this.enforcer.assertNotLocked();
2780
2948
  if (params.amount === "all" && !params.protocol) {
2781
2949
  return this.withdrawAllProtocols();
2782
2950
  }
@@ -3014,6 +3182,7 @@ var T2000 = class _T2000 extends EventEmitter {
3014
3182
  }
3015
3183
  // -- Borrowing --
3016
3184
  async borrow(params) {
3185
+ this.enforcer.assertNotLocked();
3017
3186
  const asset = "USDC";
3018
3187
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
3019
3188
  const maxResult = await adapter.maxBorrow(this._address, asset);
@@ -3046,6 +3215,7 @@ var T2000 = class _T2000 extends EventEmitter {
3046
3215
  };
3047
3216
  }
3048
3217
  async repay(params) {
3218
+ this.enforcer.assertNotLocked();
3049
3219
  const allPositions = await this.registry.allPositions(this._address);
3050
3220
  const borrows = [];
3051
3221
  for (const pos of allPositions) {
@@ -3199,6 +3369,7 @@ var T2000 = class _T2000 extends EventEmitter {
3199
3369
  }
3200
3370
  // -- Exchange --
3201
3371
  async exchange(params) {
3372
+ this.enforcer.assertNotLocked();
3202
3373
  const fromAsset = params.from;
3203
3374
  const toAsset = params.to;
3204
3375
  if (!(fromAsset in SUPPORTED_ASSETS) || !(toAsset in SUPPORTED_ASSETS)) {
@@ -3299,6 +3470,7 @@ var T2000 = class _T2000 extends EventEmitter {
3299
3470
  return this.registry.allRatesAcrossAssets();
3300
3471
  }
3301
3472
  async rebalance(opts = {}) {
3473
+ this.enforcer.assertNotLocked();
3302
3474
  const dryRun = opts.dryRun ?? false;
3303
3475
  const minYieldDiff = opts.minYieldDiff ?? 0.5;
3304
3476
  const maxBreakEven = opts.maxBreakEven ?? 30;
@@ -3580,6 +3752,7 @@ var T2000 = class _T2000 extends EventEmitter {
3580
3752
  return getSentinelInfo(this.client, id);
3581
3753
  }
3582
3754
  async sentinelAttack(id, prompt, fee) {
3755
+ this.enforcer.check({ operation: "sentinel", amount: fee ? Number(fee) / 1e9 : 0.1 });
3583
3756
  return attack(this.client, this.keypair, id, prompt, fee);
3584
3757
  }
3585
3758
  // -- Helpers --
@@ -3731,6 +3904,6 @@ var allDescriptors = [
3731
3904
  descriptor
3732
3905
  ];
3733
3906
 
3734
- export { BPS_DENOMINATOR, CLOCK_ID, CetusAdapter, DEFAULT_NETWORK, MIST_PER_SUI, NaviAdapter, ProtocolRegistry, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, SuilendAdapter, T2000, T2000Error, USDC_DECIMALS, addCollectFeeToTx, allDescriptors, calculateFee, descriptor3 as cetusDescriptor, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, getSentinelInfo, keypairFromPrivateKey, listSentinels, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, descriptor2 as naviDescriptor, rawToStable, rawToUsdc, requestAttack, saveKey, attack as sentinelAttack, descriptor as sentinelDescriptor, settleAttack, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, submitPrompt, suiToMist, descriptor4 as suilendDescriptor, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
3907
+ export { BPS_DENOMINATOR, CLOCK_ID, CetusAdapter, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, MIST_PER_SUI, NaviAdapter, OUTBOUND_OPS, ProtocolRegistry, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, SafeguardEnforcer, SafeguardError, SuilendAdapter, T2000, T2000Error, USDC_DECIMALS, addCollectFeeToTx, allDescriptors, calculateFee, descriptor3 as cetusDescriptor, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, getSentinelInfo, keypairFromPrivateKey, listSentinels, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, descriptor2 as naviDescriptor, rawToStable, rawToUsdc, requestAttack, saveKey, attack as sentinelAttack, descriptor as sentinelDescriptor, settleAttack, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, submitPrompt, suiToMist, descriptor4 as suilendDescriptor, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
3735
3908
  //# sourceMappingURL=index.js.map
3736
3909
  //# sourceMappingURL=index.js.map