@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.cjs CHANGED
@@ -12,6 +12,7 @@ var path = require('path');
12
12
  var os = require('os');
13
13
  var bcs = require('@mysten/sui/bcs');
14
14
  var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
15
+ var fs = require('fs');
15
16
 
16
17
  // src/t2000.ts
17
18
 
@@ -2496,7 +2497,10 @@ async function trySponsored(client, keypair, tx) {
2496
2497
  gasCostSui: gasCost
2497
2498
  };
2498
2499
  }
2499
- async function executeWithGas(client, keypair, buildTx) {
2500
+ async function executeWithGas(client, keypair, buildTx, options) {
2501
+ if (options?.enforcer && options?.metadata) {
2502
+ options.enforcer.check(options.metadata);
2503
+ }
2500
2504
  const errors = [];
2501
2505
  try {
2502
2506
  const tx = await buildTx();
@@ -2533,18 +2537,182 @@ async function executeWithGas(client, keypair, buildTx) {
2533
2537
  );
2534
2538
  }
2535
2539
 
2536
- // src/t2000.ts
2540
+ // src/safeguards/types.ts
2541
+ var OUTBOUND_OPS = /* @__PURE__ */ new Set([
2542
+ "send",
2543
+ "pay",
2544
+ "sentinel"
2545
+ ]);
2546
+ var DEFAULT_SAFEGUARD_CONFIG = {
2547
+ locked: false,
2548
+ maxPerTx: 0,
2549
+ maxDailySend: 0,
2550
+ dailyUsed: 0,
2551
+ dailyResetDate: ""
2552
+ };
2553
+
2554
+ // src/safeguards/errors.ts
2555
+ var SafeguardError = class extends T2000Error {
2556
+ rule;
2557
+ details;
2558
+ constructor(rule, details, message) {
2559
+ const msg = message ?? buildMessage(rule, details);
2560
+ super("SAFEGUARD_BLOCKED", msg, { rule, ...details });
2561
+ this.name = "SafeguardError";
2562
+ this.rule = rule;
2563
+ this.details = details;
2564
+ }
2565
+ toJSON() {
2566
+ return {
2567
+ error: "SAFEGUARD_BLOCKED",
2568
+ message: this.message,
2569
+ retryable: this.retryable,
2570
+ data: { rule: this.rule, ...this.details }
2571
+ };
2572
+ }
2573
+ };
2574
+ function buildMessage(rule, details) {
2575
+ switch (rule) {
2576
+ case "locked":
2577
+ return "Agent is locked. All operations are frozen.";
2578
+ case "maxPerTx":
2579
+ return `Amount $${(details.attempted ?? 0).toFixed(2)} exceeds per-transaction limit ($${(details.limit ?? 0).toFixed(2)})`;
2580
+ case "maxDailySend":
2581
+ return `Daily send limit reached ($${(details.current ?? 0).toFixed(2)}/$${(details.limit ?? 0).toFixed(2)} used today)`;
2582
+ }
2583
+ }
2584
+
2585
+ // src/safeguards/enforcer.ts
2586
+ var SafeguardEnforcer = class {
2587
+ config;
2588
+ configPath;
2589
+ constructor(configDir) {
2590
+ this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
2591
+ this.configPath = configDir ? path.join(configDir, "config.json") : null;
2592
+ }
2593
+ load() {
2594
+ if (!this.configPath) return;
2595
+ try {
2596
+ const raw = JSON.parse(fs.readFileSync(this.configPath, "utf-8"));
2597
+ this.config = {
2598
+ ...DEFAULT_SAFEGUARD_CONFIG,
2599
+ locked: raw.locked ?? false,
2600
+ maxPerTx: raw.maxPerTx ?? 0,
2601
+ maxDailySend: raw.maxDailySend ?? 0,
2602
+ dailyUsed: raw.dailyUsed ?? 0,
2603
+ dailyResetDate: raw.dailyResetDate ?? ""
2604
+ };
2605
+ } catch {
2606
+ this.config = { ...DEFAULT_SAFEGUARD_CONFIG };
2607
+ }
2608
+ }
2609
+ assertNotLocked() {
2610
+ this.load();
2611
+ if (this.config.locked) {
2612
+ throw new SafeguardError("locked", {});
2613
+ }
2614
+ }
2615
+ check(metadata) {
2616
+ this.load();
2617
+ if (this.config.locked) {
2618
+ throw new SafeguardError("locked", {});
2619
+ }
2620
+ if (!OUTBOUND_OPS.has(metadata.operation)) return;
2621
+ const amount = metadata.amount ?? 0;
2622
+ if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
2623
+ throw new SafeguardError("maxPerTx", {
2624
+ attempted: amount,
2625
+ limit: this.config.maxPerTx
2626
+ });
2627
+ }
2628
+ this.resetDailyIfNewDay();
2629
+ if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
2630
+ throw new SafeguardError("maxDailySend", {
2631
+ attempted: amount,
2632
+ limit: this.config.maxDailySend,
2633
+ current: this.config.dailyUsed
2634
+ });
2635
+ }
2636
+ }
2637
+ recordUsage(amount) {
2638
+ this.resetDailyIfNewDay();
2639
+ this.config.dailyUsed += amount;
2640
+ this.save();
2641
+ }
2642
+ lock() {
2643
+ this.config.locked = true;
2644
+ this.save();
2645
+ }
2646
+ unlock() {
2647
+ this.config.locked = false;
2648
+ this.save();
2649
+ }
2650
+ set(key, value) {
2651
+ if (key === "locked" && typeof value === "boolean") {
2652
+ this.config.locked = value;
2653
+ } else if (key === "maxPerTx" && typeof value === "number") {
2654
+ this.config.maxPerTx = value;
2655
+ } else if (key === "maxDailySend" && typeof value === "number") {
2656
+ this.config.maxDailySend = value;
2657
+ }
2658
+ this.save();
2659
+ }
2660
+ getConfig() {
2661
+ this.load();
2662
+ this.resetDailyIfNewDay();
2663
+ return { ...this.config };
2664
+ }
2665
+ isConfigured() {
2666
+ return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
2667
+ }
2668
+ resetDailyIfNewDay() {
2669
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2670
+ if (this.config.dailyResetDate !== today) {
2671
+ this.config.dailyUsed = 0;
2672
+ this.config.dailyResetDate = today;
2673
+ this.save();
2674
+ }
2675
+ }
2676
+ save() {
2677
+ if (!this.configPath) return;
2678
+ try {
2679
+ let existing = {};
2680
+ try {
2681
+ existing = JSON.parse(fs.readFileSync(this.configPath, "utf-8"));
2682
+ } catch {
2683
+ }
2684
+ const merged = {
2685
+ ...existing,
2686
+ locked: this.config.locked,
2687
+ maxPerTx: this.config.maxPerTx,
2688
+ maxDailySend: this.config.maxDailySend,
2689
+ dailyUsed: this.config.dailyUsed,
2690
+ dailyResetDate: this.config.dailyResetDate
2691
+ };
2692
+ const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
2693
+ if (!fs.existsSync(dir)) {
2694
+ fs.mkdirSync(dir, { recursive: true });
2695
+ }
2696
+ fs.writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
2697
+ } catch {
2698
+ }
2699
+ }
2700
+ };
2701
+ var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".t2000");
2537
2702
  var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2538
2703
  keypair;
2539
2704
  client;
2540
2705
  _address;
2541
2706
  registry;
2542
- constructor(keypair, client, registry) {
2707
+ enforcer;
2708
+ constructor(keypair, client, registry, configDir) {
2543
2709
  super();
2544
2710
  this.keypair = keypair;
2545
2711
  this.client = client;
2546
2712
  this._address = getAddress(keypair);
2547
2713
  this.registry = registry ?? _T2000.createDefaultRegistry(client);
2714
+ this.enforcer = new SafeguardEnforcer(configDir);
2715
+ this.enforcer.load();
2548
2716
  }
2549
2717
  static createDefaultRegistry(client) {
2550
2718
  const registry = new ProtocolRegistry();
@@ -2568,7 +2736,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2568
2736
  if (secret) {
2569
2737
  await saveKey(keypair2, secret, keyPath);
2570
2738
  }
2571
- return new _T2000(keypair2, client);
2739
+ return new _T2000(keypair2, client, void 0, DEFAULT_CONFIG_DIR);
2572
2740
  }
2573
2741
  const exists = await walletExists(keyPath);
2574
2742
  if (!exists) {
@@ -2581,7 +2749,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2581
2749
  throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
2582
2750
  }
2583
2751
  const keypair = await loadKey(secret, keyPath);
2584
- return new _T2000(keypair, client);
2752
+ return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2585
2753
  }
2586
2754
  static fromPrivateKey(privateKey, options = {}) {
2587
2755
  const keypair = keypairFromPrivateKey(privateKey);
@@ -2593,7 +2761,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2593
2761
  const keypair = generateKeypair();
2594
2762
  await saveKey(keypair, secret, options.keyPath);
2595
2763
  const client = getSuiClient();
2596
- const agent = new _T2000(keypair, client);
2764
+ const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2597
2765
  const address = agent.address();
2598
2766
  let sponsored = false;
2599
2767
  if (options.sponsored !== false) {
@@ -2619,6 +2787,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2619
2787
  return this._address;
2620
2788
  }
2621
2789
  async send(params) {
2790
+ this.enforcer.assertNotLocked();
2622
2791
  const asset = params.asset ?? "USDC";
2623
2792
  if (!(asset in SUPPORTED_ASSETS)) {
2624
2793
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
@@ -2628,8 +2797,10 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2628
2797
  const gasResult = await executeWithGas(
2629
2798
  this.client,
2630
2799
  this.keypair,
2631
- () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
2800
+ () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset }),
2801
+ { metadata: { operation: "send", amount: sendAmount }, enforcer: this.enforcer }
2632
2802
  );
2803
+ this.enforcer.recordUsage(sendAmount);
2633
2804
  const balance = await this.balance();
2634
2805
  this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
2635
2806
  return {
@@ -2685,6 +2856,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2685
2856
  }
2686
2857
  // -- Savings --
2687
2858
  async save(params) {
2859
+ this.enforcer.assertNotLocked();
2688
2860
  const asset = "USDC";
2689
2861
  const bal = await queryBalance(this.client, this._address);
2690
2862
  const usdcBalance = bal.stables.USDC ?? 0;
@@ -2779,6 +2951,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2779
2951
  };
2780
2952
  }
2781
2953
  async withdraw(params) {
2954
+ this.enforcer.assertNotLocked();
2782
2955
  if (params.amount === "all" && !params.protocol) {
2783
2956
  return this.withdrawAllProtocols();
2784
2957
  }
@@ -3016,6 +3189,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3016
3189
  }
3017
3190
  // -- Borrowing --
3018
3191
  async borrow(params) {
3192
+ this.enforcer.assertNotLocked();
3019
3193
  const asset = "USDC";
3020
3194
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
3021
3195
  const maxResult = await adapter.maxBorrow(this._address, asset);
@@ -3048,6 +3222,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3048
3222
  };
3049
3223
  }
3050
3224
  async repay(params) {
3225
+ this.enforcer.assertNotLocked();
3051
3226
  const allPositions = await this.registry.allPositions(this._address);
3052
3227
  const borrows = [];
3053
3228
  for (const pos of allPositions) {
@@ -3201,6 +3376,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3201
3376
  }
3202
3377
  // -- Exchange --
3203
3378
  async exchange(params) {
3379
+ this.enforcer.assertNotLocked();
3204
3380
  const fromAsset = params.from;
3205
3381
  const toAsset = params.to;
3206
3382
  if (!(fromAsset in SUPPORTED_ASSETS) || !(toAsset in SUPPORTED_ASSETS)) {
@@ -3301,6 +3477,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3301
3477
  return this.registry.allRatesAcrossAssets();
3302
3478
  }
3303
3479
  async rebalance(opts = {}) {
3480
+ this.enforcer.assertNotLocked();
3304
3481
  const dryRun = opts.dryRun ?? false;
3305
3482
  const minYieldDiff = opts.minYieldDiff ?? 0.5;
3306
3483
  const maxBreakEven = opts.maxBreakEven ?? 30;
@@ -3582,6 +3759,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3582
3759
  return getSentinelInfo(this.client, id);
3583
3760
  }
3584
3761
  async sentinelAttack(id, prompt, fee) {
3762
+ this.enforcer.check({ operation: "sentinel", amount: fee ? Number(fee) / 1e9 : 0.1 });
3585
3763
  return attack(this.client, this.keypair, id, prompt, fee);
3586
3764
  }
3587
3765
  // -- Helpers --
@@ -3737,12 +3915,16 @@ exports.BPS_DENOMINATOR = BPS_DENOMINATOR;
3737
3915
  exports.CLOCK_ID = CLOCK_ID;
3738
3916
  exports.CetusAdapter = CetusAdapter;
3739
3917
  exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
3918
+ exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
3740
3919
  exports.MIST_PER_SUI = MIST_PER_SUI;
3741
3920
  exports.NaviAdapter = NaviAdapter;
3921
+ exports.OUTBOUND_OPS = OUTBOUND_OPS;
3742
3922
  exports.ProtocolRegistry = ProtocolRegistry;
3743
3923
  exports.SENTINEL = SENTINEL;
3744
3924
  exports.SUI_DECIMALS = SUI_DECIMALS;
3745
3925
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
3926
+ exports.SafeguardEnforcer = SafeguardEnforcer;
3927
+ exports.SafeguardError = SafeguardError;
3746
3928
  exports.SuilendAdapter = SuilendAdapter;
3747
3929
  exports.T2000 = T2000;
3748
3930
  exports.T2000Error = T2000Error;