@t2000/sdk 0.10.4 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,182 @@ 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
+ this.load();
2609
+ if (this.config.locked) {
2610
+ throw new SafeguardError("locked", {});
2611
+ }
2612
+ }
2613
+ check(metadata) {
2614
+ this.load();
2615
+ if (this.config.locked) {
2616
+ throw new SafeguardError("locked", {});
2617
+ }
2618
+ if (!OUTBOUND_OPS.has(metadata.operation)) return;
2619
+ const amount = metadata.amount ?? 0;
2620
+ if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
2621
+ throw new SafeguardError("maxPerTx", {
2622
+ attempted: amount,
2623
+ limit: this.config.maxPerTx
2624
+ });
2625
+ }
2626
+ this.resetDailyIfNewDay();
2627
+ if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
2628
+ throw new SafeguardError("maxDailySend", {
2629
+ attempted: amount,
2630
+ limit: this.config.maxDailySend,
2631
+ current: this.config.dailyUsed
2632
+ });
2633
+ }
2634
+ }
2635
+ recordUsage(amount) {
2636
+ this.resetDailyIfNewDay();
2637
+ this.config.dailyUsed += amount;
2638
+ this.save();
2639
+ }
2640
+ lock() {
2641
+ this.config.locked = true;
2642
+ this.save();
2643
+ }
2644
+ unlock() {
2645
+ this.config.locked = false;
2646
+ this.save();
2647
+ }
2648
+ set(key, value) {
2649
+ if (key === "locked" && typeof value === "boolean") {
2650
+ this.config.locked = value;
2651
+ } else if (key === "maxPerTx" && typeof value === "number") {
2652
+ this.config.maxPerTx = value;
2653
+ } else if (key === "maxDailySend" && typeof value === "number") {
2654
+ this.config.maxDailySend = value;
2655
+ }
2656
+ this.save();
2657
+ }
2658
+ getConfig() {
2659
+ this.load();
2660
+ this.resetDailyIfNewDay();
2661
+ return { ...this.config };
2662
+ }
2663
+ isConfigured() {
2664
+ return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
2665
+ }
2666
+ resetDailyIfNewDay() {
2667
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2668
+ if (this.config.dailyResetDate !== today) {
2669
+ this.config.dailyUsed = 0;
2670
+ this.config.dailyResetDate = today;
2671
+ this.save();
2672
+ }
2673
+ }
2674
+ save() {
2675
+ if (!this.configPath) return;
2676
+ try {
2677
+ let existing = {};
2678
+ try {
2679
+ existing = JSON.parse(readFileSync(this.configPath, "utf-8"));
2680
+ } catch {
2681
+ }
2682
+ const merged = {
2683
+ ...existing,
2684
+ locked: this.config.locked,
2685
+ maxPerTx: this.config.maxPerTx,
2686
+ maxDailySend: this.config.maxDailySend,
2687
+ dailyUsed: this.config.dailyUsed,
2688
+ dailyResetDate: this.config.dailyResetDate
2689
+ };
2690
+ const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
2691
+ if (!existsSync(dir)) {
2692
+ mkdirSync(dir, { recursive: true });
2693
+ }
2694
+ writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
2695
+ } catch {
2696
+ }
2697
+ }
2698
+ };
2699
+ var DEFAULT_CONFIG_DIR = join(homedir(), ".t2000");
2535
2700
  var T2000 = class _T2000 extends EventEmitter {
2536
2701
  keypair;
2537
2702
  client;
2538
2703
  _address;
2539
2704
  registry;
2540
- constructor(keypair, client, registry) {
2705
+ enforcer;
2706
+ constructor(keypair, client, registry, configDir) {
2541
2707
  super();
2542
2708
  this.keypair = keypair;
2543
2709
  this.client = client;
2544
2710
  this._address = getAddress(keypair);
2545
2711
  this.registry = registry ?? _T2000.createDefaultRegistry(client);
2712
+ this.enforcer = new SafeguardEnforcer(configDir);
2713
+ this.enforcer.load();
2546
2714
  }
2547
2715
  static createDefaultRegistry(client) {
2548
2716
  const registry = new ProtocolRegistry();
@@ -2566,7 +2734,7 @@ var T2000 = class _T2000 extends EventEmitter {
2566
2734
  if (secret) {
2567
2735
  await saveKey(keypair2, secret, keyPath);
2568
2736
  }
2569
- return new _T2000(keypair2, client);
2737
+ return new _T2000(keypair2, client, void 0, DEFAULT_CONFIG_DIR);
2570
2738
  }
2571
2739
  const exists = await walletExists(keyPath);
2572
2740
  if (!exists) {
@@ -2579,7 +2747,7 @@ var T2000 = class _T2000 extends EventEmitter {
2579
2747
  throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
2580
2748
  }
2581
2749
  const keypair = await loadKey(secret, keyPath);
2582
- return new _T2000(keypair, client);
2750
+ return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2583
2751
  }
2584
2752
  static fromPrivateKey(privateKey, options = {}) {
2585
2753
  const keypair = keypairFromPrivateKey(privateKey);
@@ -2591,7 +2759,7 @@ var T2000 = class _T2000 extends EventEmitter {
2591
2759
  const keypair = generateKeypair();
2592
2760
  await saveKey(keypair, secret, options.keyPath);
2593
2761
  const client = getSuiClient();
2594
- const agent = new _T2000(keypair, client);
2762
+ const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2595
2763
  const address = agent.address();
2596
2764
  let sponsored = false;
2597
2765
  if (options.sponsored !== false) {
@@ -2617,6 +2785,7 @@ var T2000 = class _T2000 extends EventEmitter {
2617
2785
  return this._address;
2618
2786
  }
2619
2787
  async send(params) {
2788
+ this.enforcer.assertNotLocked();
2620
2789
  const asset = params.asset ?? "USDC";
2621
2790
  if (!(asset in SUPPORTED_ASSETS)) {
2622
2791
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
@@ -2626,8 +2795,10 @@ var T2000 = class _T2000 extends EventEmitter {
2626
2795
  const gasResult = await executeWithGas(
2627
2796
  this.client,
2628
2797
  this.keypair,
2629
- () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
2798
+ () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset }),
2799
+ { metadata: { operation: "send", amount: sendAmount }, enforcer: this.enforcer }
2630
2800
  );
2801
+ this.enforcer.recordUsage(sendAmount);
2631
2802
  const balance = await this.balance();
2632
2803
  this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
2633
2804
  return {
@@ -2683,6 +2854,7 @@ var T2000 = class _T2000 extends EventEmitter {
2683
2854
  }
2684
2855
  // -- Savings --
2685
2856
  async save(params) {
2857
+ this.enforcer.assertNotLocked();
2686
2858
  const asset = "USDC";
2687
2859
  const bal = await queryBalance(this.client, this._address);
2688
2860
  const usdcBalance = bal.stables.USDC ?? 0;
@@ -2777,6 +2949,7 @@ var T2000 = class _T2000 extends EventEmitter {
2777
2949
  };
2778
2950
  }
2779
2951
  async withdraw(params) {
2952
+ this.enforcer.assertNotLocked();
2780
2953
  if (params.amount === "all" && !params.protocol) {
2781
2954
  return this.withdrawAllProtocols();
2782
2955
  }
@@ -3014,6 +3187,7 @@ var T2000 = class _T2000 extends EventEmitter {
3014
3187
  }
3015
3188
  // -- Borrowing --
3016
3189
  async borrow(params) {
3190
+ this.enforcer.assertNotLocked();
3017
3191
  const asset = "USDC";
3018
3192
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
3019
3193
  const maxResult = await adapter.maxBorrow(this._address, asset);
@@ -3046,6 +3220,7 @@ var T2000 = class _T2000 extends EventEmitter {
3046
3220
  };
3047
3221
  }
3048
3222
  async repay(params) {
3223
+ this.enforcer.assertNotLocked();
3049
3224
  const allPositions = await this.registry.allPositions(this._address);
3050
3225
  const borrows = [];
3051
3226
  for (const pos of allPositions) {
@@ -3199,6 +3374,7 @@ var T2000 = class _T2000 extends EventEmitter {
3199
3374
  }
3200
3375
  // -- Exchange --
3201
3376
  async exchange(params) {
3377
+ this.enforcer.assertNotLocked();
3202
3378
  const fromAsset = params.from;
3203
3379
  const toAsset = params.to;
3204
3380
  if (!(fromAsset in SUPPORTED_ASSETS) || !(toAsset in SUPPORTED_ASSETS)) {
@@ -3299,6 +3475,7 @@ var T2000 = class _T2000 extends EventEmitter {
3299
3475
  return this.registry.allRatesAcrossAssets();
3300
3476
  }
3301
3477
  async rebalance(opts = {}) {
3478
+ this.enforcer.assertNotLocked();
3302
3479
  const dryRun = opts.dryRun ?? false;
3303
3480
  const minYieldDiff = opts.minYieldDiff ?? 0.5;
3304
3481
  const maxBreakEven = opts.maxBreakEven ?? 30;
@@ -3580,6 +3757,7 @@ var T2000 = class _T2000 extends EventEmitter {
3580
3757
  return getSentinelInfo(this.client, id);
3581
3758
  }
3582
3759
  async sentinelAttack(id, prompt, fee) {
3760
+ this.enforcer.check({ operation: "sentinel", amount: fee ? Number(fee) / 1e9 : 0.1 });
3583
3761
  return attack(this.client, this.keypair, id, prompt, fee);
3584
3762
  }
3585
3763
  // -- Helpers --
@@ -3731,6 +3909,6 @@ var allDescriptors = [
3731
3909
  descriptor
3732
3910
  ];
3733
3911
 
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 };
3912
+ 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
3913
  //# sourceMappingURL=index.js.map
3736
3914
  //# sourceMappingURL=index.js.map