@t2000/sdk 0.10.3 → 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.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,177 @@ 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
+ if (this.config.locked) {
2611
+ throw new SafeguardError("locked", {});
2612
+ }
2613
+ }
2614
+ check(metadata) {
2615
+ this.assertNotLocked();
2616
+ if (!OUTBOUND_OPS.has(metadata.operation)) return;
2617
+ const amount = metadata.amount ?? 0;
2618
+ if (this.config.maxPerTx > 0 && amount > this.config.maxPerTx) {
2619
+ throw new SafeguardError("maxPerTx", {
2620
+ attempted: amount,
2621
+ limit: this.config.maxPerTx
2622
+ });
2623
+ }
2624
+ this.resetDailyIfNewDay();
2625
+ if (this.config.maxDailySend > 0 && this.config.dailyUsed + amount > this.config.maxDailySend) {
2626
+ throw new SafeguardError("maxDailySend", {
2627
+ attempted: amount,
2628
+ limit: this.config.maxDailySend,
2629
+ current: this.config.dailyUsed
2630
+ });
2631
+ }
2632
+ }
2633
+ recordUsage(amount) {
2634
+ this.resetDailyIfNewDay();
2635
+ this.config.dailyUsed += amount;
2636
+ this.save();
2637
+ }
2638
+ lock() {
2639
+ this.config.locked = true;
2640
+ this.save();
2641
+ }
2642
+ unlock() {
2643
+ this.config.locked = false;
2644
+ this.save();
2645
+ }
2646
+ set(key, value) {
2647
+ if (key === "locked" && typeof value === "boolean") {
2648
+ this.config.locked = value;
2649
+ } else if (key === "maxPerTx" && typeof value === "number") {
2650
+ this.config.maxPerTx = value;
2651
+ } else if (key === "maxDailySend" && typeof value === "number") {
2652
+ this.config.maxDailySend = value;
2653
+ }
2654
+ this.save();
2655
+ }
2656
+ getConfig() {
2657
+ this.resetDailyIfNewDay();
2658
+ return { ...this.config };
2659
+ }
2660
+ isConfigured() {
2661
+ return this.config.maxPerTx > 0 || this.config.maxDailySend > 0;
2662
+ }
2663
+ resetDailyIfNewDay() {
2664
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2665
+ if (this.config.dailyResetDate !== today) {
2666
+ this.config.dailyUsed = 0;
2667
+ this.config.dailyResetDate = today;
2668
+ this.save();
2669
+ }
2670
+ }
2671
+ save() {
2672
+ if (!this.configPath) return;
2673
+ try {
2674
+ let existing = {};
2675
+ try {
2676
+ existing = JSON.parse(fs.readFileSync(this.configPath, "utf-8"));
2677
+ } catch {
2678
+ }
2679
+ const merged = {
2680
+ ...existing,
2681
+ locked: this.config.locked,
2682
+ maxPerTx: this.config.maxPerTx,
2683
+ maxDailySend: this.config.maxDailySend,
2684
+ dailyUsed: this.config.dailyUsed,
2685
+ dailyResetDate: this.config.dailyResetDate
2686
+ };
2687
+ const dir = this.configPath.replace(/[/\\][^/\\]+$/, "");
2688
+ if (!fs.existsSync(dir)) {
2689
+ fs.mkdirSync(dir, { recursive: true });
2690
+ }
2691
+ fs.writeFileSync(this.configPath, JSON.stringify(merged, null, 2) + "\n");
2692
+ } catch {
2693
+ }
2694
+ }
2695
+ };
2696
+ var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".t2000");
2537
2697
  var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2538
2698
  keypair;
2539
2699
  client;
2540
2700
  _address;
2541
2701
  registry;
2542
- constructor(keypair, client, registry) {
2702
+ enforcer;
2703
+ constructor(keypair, client, registry, configDir) {
2543
2704
  super();
2544
2705
  this.keypair = keypair;
2545
2706
  this.client = client;
2546
2707
  this._address = getAddress(keypair);
2547
2708
  this.registry = registry ?? _T2000.createDefaultRegistry(client);
2709
+ this.enforcer = new SafeguardEnforcer(configDir);
2710
+ this.enforcer.load();
2548
2711
  }
2549
2712
  static createDefaultRegistry(client) {
2550
2713
  const registry = new ProtocolRegistry();
@@ -2568,7 +2731,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2568
2731
  if (secret) {
2569
2732
  await saveKey(keypair2, secret, keyPath);
2570
2733
  }
2571
- return new _T2000(keypair2, client);
2734
+ return new _T2000(keypair2, client, void 0, DEFAULT_CONFIG_DIR);
2572
2735
  }
2573
2736
  const exists = await walletExists(keyPath);
2574
2737
  if (!exists) {
@@ -2581,7 +2744,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2581
2744
  throw new T2000Error("WALLET_LOCKED", "PIN required to unlock wallet");
2582
2745
  }
2583
2746
  const keypair = await loadKey(secret, keyPath);
2584
- return new _T2000(keypair, client);
2747
+ return new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2585
2748
  }
2586
2749
  static fromPrivateKey(privateKey, options = {}) {
2587
2750
  const keypair = keypairFromPrivateKey(privateKey);
@@ -2593,7 +2756,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2593
2756
  const keypair = generateKeypair();
2594
2757
  await saveKey(keypair, secret, options.keyPath);
2595
2758
  const client = getSuiClient();
2596
- const agent = new _T2000(keypair, client);
2759
+ const agent = new _T2000(keypair, client, void 0, DEFAULT_CONFIG_DIR);
2597
2760
  const address = agent.address();
2598
2761
  let sponsored = false;
2599
2762
  if (options.sponsored !== false) {
@@ -2619,6 +2782,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2619
2782
  return this._address;
2620
2783
  }
2621
2784
  async send(params) {
2785
+ this.enforcer.assertNotLocked();
2622
2786
  const asset = params.asset ?? "USDC";
2623
2787
  if (!(asset in SUPPORTED_ASSETS)) {
2624
2788
  throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
@@ -2628,8 +2792,10 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2628
2792
  const gasResult = await executeWithGas(
2629
2793
  this.client,
2630
2794
  this.keypair,
2631
- () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
2795
+ () => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset }),
2796
+ { metadata: { operation: "send", amount: sendAmount }, enforcer: this.enforcer }
2632
2797
  );
2798
+ this.enforcer.recordUsage(sendAmount);
2633
2799
  const balance = await this.balance();
2634
2800
  this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
2635
2801
  return {
@@ -2685,6 +2851,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2685
2851
  }
2686
2852
  // -- Savings --
2687
2853
  async save(params) {
2854
+ this.enforcer.assertNotLocked();
2688
2855
  const asset = "USDC";
2689
2856
  const bal = await queryBalance(this.client, this._address);
2690
2857
  const usdcBalance = bal.stables.USDC ?? 0;
@@ -2779,6 +2946,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2779
2946
  };
2780
2947
  }
2781
2948
  async withdraw(params) {
2949
+ this.enforcer.assertNotLocked();
2782
2950
  if (params.amount === "all" && !params.protocol) {
2783
2951
  return this.withdrawAllProtocols();
2784
2952
  }
@@ -3016,6 +3184,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3016
3184
  }
3017
3185
  // -- Borrowing --
3018
3186
  async borrow(params) {
3187
+ this.enforcer.assertNotLocked();
3019
3188
  const asset = "USDC";
3020
3189
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
3021
3190
  const maxResult = await adapter.maxBorrow(this._address, asset);
@@ -3048,6 +3217,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3048
3217
  };
3049
3218
  }
3050
3219
  async repay(params) {
3220
+ this.enforcer.assertNotLocked();
3051
3221
  const allPositions = await this.registry.allPositions(this._address);
3052
3222
  const borrows = [];
3053
3223
  for (const pos of allPositions) {
@@ -3201,6 +3371,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3201
3371
  }
3202
3372
  // -- Exchange --
3203
3373
  async exchange(params) {
3374
+ this.enforcer.assertNotLocked();
3204
3375
  const fromAsset = params.from;
3205
3376
  const toAsset = params.to;
3206
3377
  if (!(fromAsset in SUPPORTED_ASSETS) || !(toAsset in SUPPORTED_ASSETS)) {
@@ -3301,6 +3472,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3301
3472
  return this.registry.allRatesAcrossAssets();
3302
3473
  }
3303
3474
  async rebalance(opts = {}) {
3475
+ this.enforcer.assertNotLocked();
3304
3476
  const dryRun = opts.dryRun ?? false;
3305
3477
  const minYieldDiff = opts.minYieldDiff ?? 0.5;
3306
3478
  const maxBreakEven = opts.maxBreakEven ?? 30;
@@ -3582,6 +3754,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
3582
3754
  return getSentinelInfo(this.client, id);
3583
3755
  }
3584
3756
  async sentinelAttack(id, prompt, fee) {
3757
+ this.enforcer.check({ operation: "sentinel", amount: fee ? Number(fee) / 1e9 : 0.1 });
3585
3758
  return attack(this.client, this.keypair, id, prompt, fee);
3586
3759
  }
3587
3760
  // -- Helpers --
@@ -3737,12 +3910,16 @@ exports.BPS_DENOMINATOR = BPS_DENOMINATOR;
3737
3910
  exports.CLOCK_ID = CLOCK_ID;
3738
3911
  exports.CetusAdapter = CetusAdapter;
3739
3912
  exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
3913
+ exports.DEFAULT_SAFEGUARD_CONFIG = DEFAULT_SAFEGUARD_CONFIG;
3740
3914
  exports.MIST_PER_SUI = MIST_PER_SUI;
3741
3915
  exports.NaviAdapter = NaviAdapter;
3916
+ exports.OUTBOUND_OPS = OUTBOUND_OPS;
3742
3917
  exports.ProtocolRegistry = ProtocolRegistry;
3743
3918
  exports.SENTINEL = SENTINEL;
3744
3919
  exports.SUI_DECIMALS = SUI_DECIMALS;
3745
3920
  exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
3921
+ exports.SafeguardEnforcer = SafeguardEnforcer;
3922
+ exports.SafeguardError = SafeguardError;
3746
3923
  exports.SuilendAdapter = SuilendAdapter;
3747
3924
  exports.T2000 = T2000;
3748
3925
  exports.T2000Error = T2000Error;