torch-liquidation-bot 4.0.2 → 10.5.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/config.d.ts CHANGED
@@ -1,13 +1,3 @@
1
- /**
2
- * config.ts — loads environment variables into a typed BotConfig.
3
- *
4
- * env vars:
5
- * SOLANA_RPC_URL — solana RPC endpoint (required, fallback: RPC_URL)
6
- * VAULT_CREATOR — vault creator pubkey (required)
7
- * SOLANA_PRIVATE_KEY — disposable controller keypair, base58 (optional)
8
- * SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
9
- * LOG_LEVEL — debug | info | warn | error (default info)
10
- */
11
1
  import type { BotConfig } from './types';
12
2
  export declare const loadConfig: () => BotConfig;
13
3
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,SAAS,CAAA;AAIlD,eAAO,MAAM,UAAU,QAAO,SAoB7B,CAAA"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAuB,MAAM,SAAS,CAAA;AAE7D,eAAO,MAAM,UAAU,QAAO,SAgD7B,CAAA"}
package/dist/config.js CHANGED
@@ -1,34 +1,61 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = void 0;
2
4
  /**
3
5
  * config.ts — loads environment variables into a typed BotConfig.
4
- *
5
6
  * env vars:
6
- * SOLANA_RPC_URL — solana RPC endpoint (required, fallback: RPC_URL)
7
- * VAULT_CREATOR — vault creator pubkey (required)
8
- * SOLANA_PRIVATE_KEY — disposable controller keypair, base58 (optional)
9
- * SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
10
- * LOG_LEVEL debug | info | warn | error (default info)
7
+ * SOLANA_RPC_URL — solana RPC endpoint (required, fallback: RPC_URL)
8
+ * VAULT_CREATOR — vault creator pubkey (required)
9
+ * SOLANA_PRIVATE_KEY — disposable controller keypair, base58 or JSON byte array (optional)
10
+ * SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
11
+ * SCAN_LIMIT max tokens to scan per cycle (default 50, 0 = unlimited)
12
+ * MIN_AGENT_BALANCE_SOL — pause liquidations below this balance (default 0.01)
13
+ * LOG_LEVEL — debug | info | warn | error (default info)
14
+ * LOG_FORMAT — text | json (default text)
11
15
  */
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.loadConfig = void 0;
14
- const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
16
+ const torchsdk_1 = require("torchsdk");
17
+ const constants_1 = require("./constants");
15
18
  const loadConfig = () => {
16
19
  const rpcUrl = process.env.SOLANA_RPC_URL ?? process.env.RPC_URL;
17
- if (!rpcUrl)
20
+ if (!rpcUrl) {
18
21
  throw new Error('SOLANA_RPC_URL env var is required (fallback: RPC_URL)');
22
+ }
19
23
  const vaultCreator = process.env.VAULT_CREATOR;
20
- if (!vaultCreator)
24
+ if (!vaultCreator) {
21
25
  throw new Error('VAULT_CREATOR env var is required (vault creator pubkey)');
26
+ }
22
27
  const privateKey = process.env.SOLANA_PRIVATE_KEY ?? null;
23
28
  const scanIntervalMs = parseInt(process.env.SCAN_INTERVAL_MS ?? '30000', 10);
24
29
  if (isNaN(scanIntervalMs) || scanIntervalMs < 5000) {
25
30
  throw new Error('SCAN_INTERVAL_MS must be a number >= 5000');
26
31
  }
32
+ const scanLimit = parseInt(process.env.SCAN_LIMIT ?? '50', 10);
33
+ if (isNaN(scanLimit) || scanLimit < 0) {
34
+ throw new Error('SCAN_LIMIT must be a non-negative number (0 = unlimited)');
35
+ }
36
+ const minAgentBalanceSol = parseFloat(process.env.MIN_AGENT_BALANCE_SOL ?? '0.01');
37
+ if (isNaN(minAgentBalanceSol) || minAgentBalanceSol < 0) {
38
+ throw new Error('MIN_AGENT_BALANCE_SOL must be a non-negative number');
39
+ }
40
+ const minAgentBalanceLamports = Math.floor(minAgentBalanceSol * torchsdk_1.LAMPORTS_PER_SOL);
27
41
  const logLevel = (process.env.LOG_LEVEL ?? 'info');
28
- if (!LOG_LEVELS.includes(logLevel)) {
29
- throw new Error(`LOG_LEVEL must be one of: ${LOG_LEVELS.join(', ')}`);
42
+ if (!constants_1.LOG_LEVELS.includes(logLevel)) {
43
+ throw new Error(`LOG_LEVEL must be one of: ${constants_1.LOG_LEVELS.join(', ')}`);
44
+ }
45
+ const logFormat = (process.env.LOG_FORMAT ?? 'text');
46
+ if (!constants_1.LOG_FORMATS.includes(logFormat)) {
47
+ throw new Error(`LOG_FORMAT must be one of: ${constants_1.LOG_FORMATS.join(', ')}`);
30
48
  }
31
- return { rpcUrl, vaultCreator, privateKey, scanIntervalMs, logLevel };
49
+ return {
50
+ rpcUrl,
51
+ vaultCreator,
52
+ privateKey,
53
+ scanIntervalMs,
54
+ scanLimit,
55
+ minAgentBalanceLamports,
56
+ logLevel,
57
+ logFormat,
58
+ };
32
59
  };
33
60
  exports.loadConfig = loadConfig;
34
61
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAIH,MAAM,UAAU,GAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAE1D,MAAM,UAAU,GAAG,GAAc,EAAE;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAA;IAChE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAEtF,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IAC9C,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAE9F,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAA;IAEzD,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,EAAE,EAAE,CAAC,CAAA;IAC5E,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;IAC9D,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAa,CAAA;IAC9D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACvE,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAA;AACvE,CAAC,CAAA;AApBY,QAAA,UAAU,cAoBtB"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,uCAA2C;AAE3C,2CAAqD;AAG9C,MAAM,UAAU,GAAG,GAAc,EAAE;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAA;IAChE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IAC9C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,CAAA;IACzD,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,EAAE,EAAE,CAAC,CAAA;IAC5E,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;IAC9D,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,EAAE,EAAE,CAAC,CAAA;IAC9D,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IAED,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,CAAA;IAClF,IAAI,KAAK,CAAC,kBAAkB,CAAC,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,2BAAgB,CAAC,CAAA;IACjF,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAa,CAAA;IAC9D,IAAI,CAAC,sBAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,6BAA6B,sBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACvE,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAc,CAAA;IACjE,IAAI,CAAC,uBAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,8BAA8B,uBAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACzE,CAAC;IAED,OAAO;QACL,MAAM;QACN,YAAY;QACZ,UAAU;QACV,cAAc;QACd,SAAS;QACT,uBAAuB;QACvB,QAAQ;QACR,SAAS;KACV,CAAA;AACH,CAAC,CAAA;AAhDY,QAAA,UAAU,cAgDtB"}
@@ -0,0 +1,7 @@
1
+ import { LogFormat, LogLevel } from './types';
2
+ export declare const DEFAULT_RETRY_ATTEMPTS = 3;
3
+ export declare const LOG_LEVELS: LogLevel[];
4
+ export declare const LOG_FORMATS: LogFormat[];
5
+ export declare const RETRY_BASE_DELAY_MS = 1000;
6
+ export declare const RPC_TIMEOUT_MS = 30000;
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAE7C,eAAO,MAAM,sBAAsB,IAAI,CAAA;AACvC,eAAO,MAAM,UAAU,EAAE,QAAQ,EAAuC,CAAA;AACxE,eAAO,MAAM,WAAW,EAAE,SAAS,EAAqB,CAAA;AACxD,eAAO,MAAM,mBAAmB,OAAQ,CAAA;AACxC,eAAO,MAAM,cAAc,QAAS,CAAA"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RPC_TIMEOUT_MS = exports.RETRY_BASE_DELAY_MS = exports.LOG_FORMATS = exports.LOG_LEVELS = exports.DEFAULT_RETRY_ATTEMPTS = void 0;
4
+ exports.DEFAULT_RETRY_ATTEMPTS = 3;
5
+ exports.LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
6
+ exports.LOG_FORMATS = ['text', 'json'];
7
+ exports.RETRY_BASE_DELAY_MS = 1000;
8
+ exports.RPC_TIMEOUT_MS = 30000;
9
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAEa,QAAA,sBAAsB,GAAG,CAAC,CAAA;AAC1B,QAAA,UAAU,GAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAC3D,QAAA,WAAW,GAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3C,QAAA,mBAAmB,GAAG,IAAK,CAAA;AAC3B,QAAA,cAAc,GAAG,KAAM,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,19 +1,3 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * torch-liquidation-bot — vault-based liquidation bot.
4
- *
5
- * generates an agent keypair in-process (or uses SOLANA_PRIVATE_KEY if provided).
6
- * all operations route through a torch vault identified by VAULT_CREATOR.
7
- *
8
- * usage:
9
- * VAULT_CREATOR=<pubkey> SOLANA_RPC_URL=<rpc> npx tsx src/index.ts
10
- *
11
- * env:
12
- * SOLANA_RPC_URL — solana RPC endpoint (required, fallback: RPC_URL)
13
- * VAULT_CREATOR — vault creator pubkey (required)
14
- * SOLANA_PRIVATE_KEY — disposable controller keypair, base58 (optional)
15
- * SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
16
- * LOG_LEVEL — debug | info | warn | error (default info)
17
- */
18
2
  export {};
19
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -1,115 +1,151 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
3
7
  /**
4
- * torch-liquidation-bot — vault-based liquidation bot.
5
- *
6
- * generates an agent keypair in-process (or uses SOLANA_PRIVATE_KEY if provided).
7
- * all operations route through a torch vault identified by VAULT_CREATOR.
8
+ * torch-liquidation-bot — vault-based liquidation keeper.
8
9
  *
10
+ * generates an agent keypair in-process (or loads SOLANA_PRIVATE_KEY).
11
+ * all value flows through a Torch Vault identified by VAULT_CREATOR.
12
+ * the agent key is a stateless signer holding only gas SOL.
9
13
  * usage:
10
- * VAULT_CREATOR=<pubkey> SOLANA_RPC_URL=<rpc> npx tsx src/index.ts
11
- *
12
- * env:
13
- * SOLANA_RPC_URL — solana RPC endpoint (required, fallback: RPC_URL)
14
- * VAULT_CREATOR — vault creator pubkey (required)
15
- * SOLANA_PRIVATE_KEY — disposable controller keypair, base58 (optional)
16
- * SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
17
- * LOG_LEVEL — debug | info | warn | error (default info)
14
+ * VAULT_CREATOR=<pubkey> SOLANA_RPC_URL=<rpc> npx torch-liquidation-bot
15
+ * See config.ts for the full env var list.
18
16
  */
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
17
  const web3_js_1 = require("@solana/web3.js");
18
+ const bs58_1 = __importDefault(require("bs58"));
21
19
  const torchsdk_1 = require("torchsdk");
22
20
  const config_1 = require("./config");
23
21
  const utils_1 = require("./utils");
24
- // ---------------------------------------------------------------------------
25
- // scan & liquidate
26
- // ---------------------------------------------------------------------------
27
- const scanAndLiquidate = async (connection, log, vaultCreator, agentKeypair) => {
28
- const { tokens } = await (0, utils_1.withTimeout)((0, torchsdk_1.getTokens)(connection, { status: 'migrated', sort: 'volume', limit: 50 }), 'getTokens');
22
+ const scanAndLiquidate = async ({ connection, log, vaultCreator, agentKeypair, agentPk, scanLimit, minBalance, stats, isShutdownRequested, }) => {
23
+ // pre-flight balance check — skip cycle if agent can't pay gas
24
+ const balance = await (0, utils_1.withRetry)(() => (0, utils_1.withTimeout)(connection.getBalance(agentKeypair.publicKey), 'getBalance'), 'getBalance', 3, () => stats.rpcRetries++);
25
+ if (balance < minBalance) {
26
+ log('error', `agent balance too low pausing cycle`, {
27
+ balance_sol: (0, utils_1.sol)(balance),
28
+ min_sol: (0, utils_1.sol)(minBalance),
29
+ });
30
+ return;
31
+ }
32
+ const { tokens } = await (0, utils_1.withRetry)(() => (0, utils_1.withTimeout)((0, torchsdk_1.getTokens)(connection, {
33
+ status: 'migrated',
34
+ sort: 'volume',
35
+ limit: scanLimit > 0 ? scanLimit : 10000,
36
+ }), 'getTokens'), 'getTokens', 3, () => stats.rpcRetries++);
29
37
  log('debug', `discovered ${tokens.length} migrated tokens`);
30
38
  for (const token of tokens) {
39
+ // bail between tokens so SIGTERM doesn't stall on a long scan of many tokens
40
+ if (isShutdownRequested())
41
+ return;
31
42
  let positions;
32
43
  try {
33
- const result = await (0, utils_1.withTimeout)((0, torchsdk_1.getAllLoanPositions)(connection, token.mint), 'getAllLoanPositions');
44
+ const result = await (0, utils_1.withRetry)(() => (0, utils_1.withTimeout)((0, torchsdk_1.getAllLoanPositions)(connection, token.mint), 'getAllLoanPositions'), 'getAllLoanPositions', 3, () => stats.rpcRetries++);
34
45
  positions = result.positions;
35
46
  }
36
47
  catch {
37
- continue; // lending not enabled for this token
48
+ continue; // lending not enabled for this token, or persistent RPC failure
38
49
  }
39
- if (positions.length === 0)
50
+ if (positions.length === 0) {
40
51
  continue;
52
+ }
41
53
  log('debug', `${token.symbol} — ${positions.length} active loans`);
42
- // positions are pre-sorted: liquidatable at_risk healthy
54
+ // positions are pre-sorted: liquidatable -> at_risk -> healthy
43
55
  for (const position of positions) {
44
- if (position.health !== 'liquidatable')
45
- break; // sorted, so no more liquidatable after this
46
- log('info', `LIQUIDATABLE | ${token.symbol} | borrower=${position.borrower.slice(0, 8)}... | ` +
47
- `LTV=${position.current_ltv_bps != null ? (0, utils_1.bpsToPercent)(position.current_ltv_bps) : '?'} | ` +
48
- `owed=${(0, utils_1.sol)(position.total_owed)} SOL`);
49
- // build and execute liquidation through the vault
56
+ if (position.health !== 'liquidatable') {
57
+ break;
58
+ }
59
+ log('info', `LIQUIDATABLE`, {
60
+ token: token.symbol,
61
+ borrower: position.borrower.slice(0, 8) + '...',
62
+ ltv: position.current_ltv_bps != null ? (0, utils_1.bpsToPercent)(position.current_ltv_bps) : 'unknown',
63
+ owed_sol: (0, utils_1.sol)(position.total_owed),
64
+ });
50
65
  try {
51
66
  const { transaction, message } = await (0, utils_1.withTimeout)((0, torchsdk_1.buildLiquidateTransaction)(connection, {
52
67
  mint: token.mint,
53
- liquidator: agentKeypair.publicKey.toBase58(),
68
+ liquidator: agentPk,
54
69
  borrower: position.borrower,
55
70
  vault: vaultCreator,
56
71
  }), 'buildLiquidateTransaction');
57
- transaction.sign(agentKeypair);
72
+ transaction.sign([agentKeypair]);
58
73
  const signature = await connection.sendRawTransaction(transaction.serialize());
59
- await (0, utils_1.withTimeout)((0, torchsdk_1.confirmTransaction)(connection, signature, agentKeypair.publicKey.toBase58()), 'confirmTransaction');
60
- log('info', `LIQUIDATED | ${token.symbol} | borrower=${position.borrower.slice(0, 8)}... | ` +
61
- `sig=${signature.slice(0, 16)}... | ${message}`);
74
+ await (0, utils_1.withTimeout)((0, torchsdk_1.confirmTransaction)(connection, signature, agentPk), 'confirmTransaction');
75
+ stats.liquidations++;
76
+ log('info', `LIQUIDATED`, {
77
+ token: token.symbol,
78
+ borrower: position.borrower.slice(0, 8) + '...',
79
+ sig: signature.slice(0, 16) + '...',
80
+ message,
81
+ });
62
82
  }
63
83
  catch (err) {
64
- log('warn', `LIQUIDATION FAILED | ${token.symbol} | ${position.borrower.slice(0, 8)}... | ${err.message}`);
84
+ stats.failures++;
85
+ const msg = err instanceof Error ? err.message : String(err);
86
+ stats.lastError = msg;
87
+ log('warn', `LIQUIDATION FAILED`, {
88
+ token: token.symbol,
89
+ borrower: position.borrower.slice(0, 8) + '...',
90
+ error: msg,
91
+ });
65
92
  }
66
93
  }
67
94
  }
68
95
  };
69
- // ---------------------------------------------------------------------------
70
- // main — vault-routed liquidation loop
71
- // ---------------------------------------------------------------------------
72
- const main = async () => {
73
- const config = (0, config_1.loadConfig)();
74
- const log = (0, utils_1.createLogger)(config.logLevel);
75
- const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
76
- // load or generate agent keypair
77
- let agentKeypair;
78
- if (config.privateKey) {
79
- try {
80
- const parsed = JSON.parse(config.privateKey);
81
- if (Array.isArray(parsed)) {
82
- agentKeypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(parsed));
83
- }
84
- else {
85
- throw new Error('SOLANA_PRIVATE_KEY JSON must be a byte array');
86
- }
87
- }
88
- catch (e) {
89
- if (e.message?.includes('byte array'))
90
- throw e;
91
- // not JSON — try base58
92
- agentKeypair = web3_js_1.Keypair.fromSecretKey((0, utils_1.decodeBase58)(config.privateKey));
96
+ const loadAgentKeypair = (privateKey, log) => {
97
+ if (!privateKey) {
98
+ const kp = web3_js_1.Keypair.generate();
99
+ log('info', 'generated fresh agent keypair');
100
+ return kp;
101
+ }
102
+ try {
103
+ const parsed = JSON.parse(privateKey);
104
+ if (Array.isArray(parsed)) {
105
+ const kp = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(parsed));
106
+ log('info', 'loaded keypair from SOLANA_PRIVATE_KEY (JSON byte array)');
107
+ return kp;
93
108
  }
94
- log('info', 'loaded keypair from SOLANA_PRIVATE_KEY');
109
+ throw new Error('SOLANA_PRIVATE_KEY JSON must be a byte array');
95
110
  }
96
- else {
97
- agentKeypair = web3_js_1.Keypair.generate();
98
- log('info', 'generated fresh agent keypair');
111
+ catch (e) {
112
+ const msg = e instanceof Error ? e.message : String(e);
113
+ if (msg.includes('byte array'))
114
+ throw e;
99
115
  }
116
+ const kp = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(privateKey));
117
+ log('info', 'loaded keypair from SOLANA_PRIVATE_KEY (base58)');
118
+ return kp;
119
+ };
120
+ // main — vault-routed liquidation loop with graceful shutdown
121
+ const main = async () => {
122
+ const config = (0, config_1.loadConfig)();
123
+ const log = (0, utils_1.createLogger)(config.logLevel, config.logFormat);
124
+ const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
125
+ const agentKeypair = loadAgentKeypair(config.privateKey, log);
126
+ const agentPk = agentKeypair.publicKey.toBase58();
127
+ const stats = {
128
+ cycles: 0,
129
+ liquidations: 0,
130
+ failures: 0,
131
+ rpcRetries: 0,
132
+ startedAt: Date.now(),
133
+ lastError: null,
134
+ };
100
135
  console.log('=== torch liquidation bot ===');
101
- console.log(`agent wallet: ${agentKeypair.publicKey.toBase58()}`);
136
+ console.log(`agent wallet: ${agentPk}`);
102
137
  console.log(`vault creator: ${config.vaultCreator}`);
103
138
  console.log(`scan interval: ${config.scanIntervalMs}ms`);
104
- console.log();
139
+ console.log(`scan limit: ${config.scanLimit === 0 ? 'unlimited' : config.scanLimit}`);
140
+ console.log(`min agent balance: ${(0, utils_1.sol)(config.minAgentBalanceLamports)} SOL\n`);
105
141
  // verify vault exists
106
- const vault = await (0, utils_1.withTimeout)((0, torchsdk_1.getVault)(connection, config.vaultCreator), 'getVault');
142
+ const vault = await (0, utils_1.withRetry)(() => (0, utils_1.withTimeout)((0, torchsdk_1.getVault)(connection, config.vaultCreator), 'getVault'), 'getVault', 3, () => stats.rpcRetries++);
107
143
  if (!vault) {
108
144
  throw new Error(`vault not found for creator ${config.vaultCreator}`);
109
145
  }
110
146
  log('info', `vault found — authority=${vault.authority}`);
111
147
  // verify agent wallet is linked to vault
112
- const link = await (0, utils_1.withTimeout)((0, torchsdk_1.getVaultForWallet)(connection, agentKeypair.publicKey.toBase58()), 'getVaultForWallet');
148
+ const link = await (0, utils_1.withRetry)(() => (0, utils_1.withTimeout)((0, torchsdk_1.getVaultForWallet)(connection, agentPk), 'getVaultForWallet'), 'getVaultForWallet', 3, () => stats.rpcRetries++);
113
149
  if (!link) {
114
150
  console.log();
115
151
  console.log('--- ACTION REQUIRED ---');
@@ -119,30 +155,74 @@ const main = async () => {
119
155
  console.log(` buildLinkWalletTransaction(connection, {`);
120
156
  console.log(` authority: "<your-authority-pubkey>",`);
121
157
  console.log(` vault_creator: "${config.vaultCreator}",`);
122
- console.log(` wallet_to_link: "${agentKeypair.publicKey.toBase58()}"`);
158
+ console.log(` wallet_to_link: "${agentPk}"`);
123
159
  console.log(` })`);
124
160
  console.log();
125
161
  console.log('then restart the bot.');
126
162
  console.log('-----------------------');
127
163
  process.exit(1);
128
164
  }
129
- log('info', 'agent wallet linked to vault — starting scan loop');
165
+ log('info', `agent wallet linked to vault — starting scan loop`);
130
166
  log('info', `treasury: ${(0, utils_1.sol)(vault.sol_balance ?? 0)} SOL`);
167
+ // graceful shutdown on SIGINT / SIGTERM
168
+ let shutdown = false;
169
+ const requestShutdown = (signal) => {
170
+ if (shutdown)
171
+ return;
172
+ shutdown = true;
173
+ log('info', `received ${signal} — shutting down after current cycle`);
174
+ };
175
+ process.on('SIGINT', () => requestShutdown('SIGINT'));
176
+ process.on('SIGTERM', () => requestShutdown('SIGTERM'));
177
+ const ctx = {
178
+ connection,
179
+ log,
180
+ vaultCreator: config.vaultCreator,
181
+ agentKeypair,
182
+ agentPk,
183
+ scanLimit: config.scanLimit,
184
+ minBalance: config.minAgentBalanceLamports,
185
+ stats,
186
+ isShutdownRequested: () => shutdown,
187
+ };
131
188
  // scan loop
132
- while (true) {
189
+ while (!shutdown) {
133
190
  try {
134
191
  log('debug', '--- scan cycle start ---');
135
- await scanAndLiquidate(connection, log, config.vaultCreator, agentKeypair);
136
- log('debug', '--- scan cycle end ---');
192
+ await scanAndLiquidate(ctx);
193
+ stats.cycles++;
194
+ log('info', `stats`, {
195
+ cycles: stats.cycles,
196
+ liquidations: stats.liquidations,
197
+ failures: stats.failures,
198
+ rpc_retries: stats.rpcRetries,
199
+ uptime_sec: Math.floor((Date.now() - stats.startedAt) / 1000),
200
+ });
137
201
  }
138
202
  catch (err) {
139
- log('error', `scan cycle error: ${err.message}`);
203
+ const msg = err instanceof Error ? err.message : String(err);
204
+ stats.lastError = msg;
205
+ log('error', `scan cycle error: ${msg}`);
206
+ }
207
+ if (shutdown) {
208
+ break;
209
+ }
210
+ // interruptible sleep so shutdown fires quickly
211
+ const sleepUntil = Date.now() + config.scanIntervalMs;
212
+ while (!shutdown && Date.now() < sleepUntil) {
213
+ await new Promise((resolve) => setTimeout(resolve, 500));
140
214
  }
141
- await new Promise((resolve) => setTimeout(resolve, config.scanIntervalMs));
142
215
  }
216
+ log('info', 'graceful shutdown complete', {
217
+ final_cycles: stats.cycles,
218
+ final_liquidations: stats.liquidations,
219
+ final_failures: stats.failures,
220
+ });
221
+ process.exit(0);
143
222
  };
144
223
  main().catch((err) => {
145
- console.error('FATAL:', err.message ?? err);
224
+ const msg = err instanceof Error ? err.message : String(err);
225
+ console.error('FATAL:', msg);
146
226
  process.exit(1);
147
227
  });
148
228
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,12 +1,34 @@
1
- /**
2
- * types.ts — interfaces for the vault-based liquidation bot.
3
- */
1
+ import { Connection, Keypair } from '@solana/web3.js';
4
2
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
3
+ export type LogFormat = 'text' | 'json';
4
+ export type Logger = (level: LogLevel, msg: string, extra?: Record<string, unknown>) => void;
5
5
  export interface BotConfig {
6
6
  rpcUrl: string;
7
7
  vaultCreator: string;
8
8
  privateKey: string | null;
9
9
  scanIntervalMs: number;
10
10
  logLevel: LogLevel;
11
+ logFormat: LogFormat;
12
+ scanLimit: number;
13
+ minAgentBalanceLamports: number;
14
+ }
15
+ export interface BotStats {
16
+ cycles: number;
17
+ liquidations: number;
18
+ failures: number;
19
+ rpcRetries: number;
20
+ startedAt: number;
21
+ lastError: string | null;
22
+ }
23
+ export interface ScanContext {
24
+ connection: Connection;
25
+ log: Logger;
26
+ vaultCreator: string;
27
+ agentKeypair: Keypair;
28
+ agentPk: string;
29
+ scanLimit: number;
30
+ minBalance: number;
31
+ stats: BotStats;
32
+ isShutdownRequested: () => boolean;
11
33
  }
12
34
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE1D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,QAAQ,CAAA;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAC1D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;AACvC,MAAM,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAE5F,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,SAAS,CAAA;IAEpB,SAAS,EAAE,MAAM,CAAA;IAEjB,uBAAuB,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAA;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,QAAQ,CAAA;IAGf,mBAAmB,EAAE,MAAM,OAAO,CAAA;CACnC"}
package/dist/types.js CHANGED
@@ -1,6 +1,3 @@
1
1
  "use strict";
2
- /**
3
- * types.ts — interfaces for the vault-based liquidation bot.
4
- */
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/dist/utils.d.ts CHANGED
@@ -1,10 +1,7 @@
1
- /**
2
- * utils.ts — shared helpers.
3
- */
4
- import type { LogLevel } from './types';
1
+ import type { LogFormat, LogLevel, Logger } from './types';
5
2
  export declare const sol: (lamports: number) => string;
6
3
  export declare const bpsToPercent: (bps: number) => string;
7
- export declare const decodeBase58: (s: string) => Uint8Array;
8
- export declare function withTimeout<T>(promise: Promise<T>, label: string): Promise<T>;
9
- export declare function createLogger(minLevel: LogLevel): (level: LogLevel, msg: string) => void;
4
+ export declare const withTimeout: <T>(promise: Promise<T>, label: string) => Promise<T>;
5
+ export declare const withRetry: <T>(fn: () => Promise<T>, label: string, attempts?: number, onRetry?: () => void) => Promise<T>;
6
+ export declare const createLogger: (minLevel: LogLevel, format?: LogFormat) => Logger;
10
7
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,eAAO,MAAM,GAAG,GAAI,UAAU,MAAM,WAA6C,CAAA;AAEjF,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,WAAiC,CAAA;AAKzE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,UAmBxC,CAAA;AAID,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAO7E;AAID,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,IAGzB,OAAO,QAAQ,EAAE,KAAK,MAAM,UAMjD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAQ1D,eAAO,MAAM,GAAG,GAAI,UAAU,MAAM,WAA6C,CAAA;AACjF,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,WAAiC,CAAA;AAEzE,eAAO,MAAM,WAAW,GAAU,CAAC,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,CAAC,CAUlF,CAAA;AAKD,eAAO,MAAM,SAAS,GAAU,CAAC,EAC/B,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,MAAM,EACb,iBAAiC,EACjC,UAAU,MAAM,IAAI,KACnB,OAAO,CAAC,CAAC,CAcX,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ,EAAE,SAAQ,SAAkB,KAAG,MAe7E,CAAA"}
package/dist/utils.js CHANGED
@@ -1,56 +1,54 @@
1
1
  "use strict";
2
- /**
3
- * utils.ts — shared helpers.
4
- */
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.decodeBase58 = exports.bpsToPercent = exports.sol = void 0;
7
- exports.withTimeout = withTimeout;
8
- exports.createLogger = createLogger;
3
+ exports.createLogger = exports.withRetry = exports.withTimeout = exports.bpsToPercent = exports.sol = void 0;
9
4
  const torchsdk_1 = require("torchsdk");
5
+ const constants_1 = require("./constants");
10
6
  const sol = (lamports) => (lamports / torchsdk_1.LAMPORTS_PER_SOL).toFixed(4);
11
7
  exports.sol = sol;
12
8
  const bpsToPercent = (bps) => (bps / 100).toFixed(2) + '%';
13
9
  exports.bpsToPercent = bpsToPercent;
14
- // base58 decoder avoids ESM-only bs58 dependency
15
- const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
16
- const decodeBase58 = (s) => {
17
- const result = [];
18
- for (let i = 0; i < s.length; i++) {
19
- let carry = B58.indexOf(s[i]);
20
- if (carry < 0)
21
- throw new Error(`invalid base58 character: ${s[i]}`);
22
- for (let j = 0; j < result.length; j++) {
23
- carry += result[j] * 58;
24
- result[j] = carry & 0xff;
25
- carry >>= 8;
10
+ const withTimeout = async (promise, label) => {
11
+ return Promise.race([
12
+ promise,
13
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${constants_1.RPC_TIMEOUT_MS}ms`)), constants_1.RPC_TIMEOUT_MS)),
14
+ ]);
15
+ };
16
+ exports.withTimeout = withTimeout;
17
+ // retry an async operation with exponential backoff.
18
+ // retries transient failures up to `attempts` times with 1s, 2s, 4s delays.
19
+ // re-throws the last error if all attempts fail. Set `onRetry` to track retry counts in metrics.
20
+ const withRetry = async (fn, label, attempts = constants_1.DEFAULT_RETRY_ATTEMPTS, onRetry) => {
21
+ let lastErr = new Error(`${label} failed with no attempts`);
22
+ for (let i = 0; i < attempts; i++) {
23
+ try {
24
+ return await fn();
26
25
  }
27
- while (carry > 0) {
28
- result.push(carry & 0xff);
29
- carry >>= 8;
26
+ catch (err) {
27
+ lastErr = err;
28
+ if (i < attempts - 1) {
29
+ onRetry?.();
30
+ await new Promise((r) => setTimeout(r, constants_1.RETRY_BASE_DELAY_MS * Math.pow(2, i)));
31
+ }
30
32
  }
31
33
  }
32
- for (let i = 0; i < s.length && s[i] === '1'; i++) {
33
- result.push(0);
34
- }
35
- return new Uint8Array(result.reverse());
34
+ throw lastErr;
36
35
  };
37
- exports.decodeBase58 = decodeBase58;
38
- const RPC_TIMEOUT_MS = 30000;
39
- function withTimeout(promise, label) {
40
- return Promise.race([
41
- promise,
42
- new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${RPC_TIMEOUT_MS}ms`)), RPC_TIMEOUT_MS)),
43
- ]);
44
- }
45
- const LEVEL_ORDER = ['debug', 'info', 'warn', 'error'];
46
- function createLogger(minLevel) {
47
- const minIdx = LEVEL_ORDER.indexOf(minLevel);
48
- return function log(level, msg) {
49
- if (LEVEL_ORDER.indexOf(level) < minIdx)
36
+ exports.withRetry = withRetry;
37
+ const createLogger = (minLevel, format = 'text') => {
38
+ const minIdx = constants_1.LOG_LEVELS.indexOf(minLevel);
39
+ return (level, msg, extra) => {
40
+ if (constants_1.LOG_LEVELS.indexOf(level) < minIdx)
41
+ return;
42
+ const ts = new Date().toISOString();
43
+ if (format === 'json') {
44
+ console.log(JSON.stringify({ ts, level, msg, ...(extra ?? {}) }));
50
45
  return;
51
- const ts = new Date().toISOString().substr(11, 12);
46
+ }
47
+ const tsShort = ts.substr(11, 12);
52
48
  const tag = level.toUpperCase().padEnd(5);
53
- console.log(`[${ts}] ${tag} ${msg}`);
49
+ const extraStr = extra ? ' ' + JSON.stringify(extra) : '';
50
+ console.log(`[${tsShort}] ${tag} ${msg}${extraStr}`);
54
51
  };
55
- }
52
+ };
53
+ exports.createLogger = createLogger;
56
54
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torch-liquidation-bot",
3
- "version": "4.0.2",
3
+ "version": "10.5.0",
4
4
  "description": "autonomous vault-based liquidation keeper for Torch Market lending on Solana using torchsdk",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,13 +17,15 @@
17
17
  "format": "prettier --write src/ tests/"
18
18
  },
19
19
  "dependencies": {
20
+ "@solana/spl-token": "^0.4.14",
20
21
  "@solana/web3.js": "1.98.4",
21
- "torchsdk": "3.7.23"
22
+ "bs58": "^6.0.0",
23
+ "torchsdk": "10.5.0"
22
24
  },
23
25
  "devDependencies": {
24
- "@types/node": "20.19.33",
25
- "prettier": "3.8.1",
26
- "typescript": "5.9.3"
26
+ "@types/node": "^25.6.0",
27
+ "prettier": "^3.8.3",
28
+ "typescript": "^6.0.3"
27
29
  },
28
30
  "peerDependencies": {
29
31
  "@solana/web3.js": "^1.98.0"
package/readme.md CHANGED
@@ -1,25 +1,23 @@
1
- # torch-liquidation-bot v4.0.2 (Vault Mode)
1
+ # torch-liquidation-bot
2
2
 
3
- Vault-based liquidation bot for [Torch Market](https://torch.market) on Solana. Generates an agent keypair in-process no user wallet required. All operations route through a Torch Vault.
4
-
5
- > **v3.0.0+ Breaking Change:** The bot now operates through the torchsdk v3.7.22 vault model. It generates a disposable agent keypair at startup, scans for underwater loan positions using the SDK's bulk loan scanner (`getAllLoanPositions`), and executes liquidations. The user never provides a wallet — only a vault creator pubkey and an RPC endpoint.
3
+ Vault-based liquidation keeper for [Torch Market](https://torch.market) on Solana. Generates an agent keypair in-process -- no user wallet required. All operations route through a Torch Vault.
6
4
 
7
5
  ## Install
8
6
 
9
7
  ```bash
10
- npm install torch-liquidation-bot@4.0.2
8
+ npm install torch-liquidation-bot
11
9
  ```
12
10
 
13
11
  ## Quick Start
14
12
 
15
13
  ```bash
16
- # 1. start the bot it prints its agent wallet on startup
14
+ # 1. start the bot -- it prints its agent wallet on startup
17
15
  VAULT_CREATOR=<your-vault-creator-pubkey> SOLANA_RPC_URL=<rpc> npx torch-liquidation-bot
18
16
 
19
17
  # 2. link the printed agent wallet to your vault (one-time, from your authority wallet)
20
18
  # the bot will print the exact instructions if the wallet is not yet linked
21
19
 
22
- # 3. restart the bot it will begin scanning and liquidating
20
+ # 3. restart the bot -- it will begin scanning and liquidating
23
21
  ```
24
22
 
25
23
  ## What It Does
@@ -30,7 +28,7 @@ This bot:
30
28
 
31
29
  1. Generates a disposable `Keypair` in-process (no private key leaves the process)
32
30
  2. Verifies the vault exists and the agent wallet is linked
33
- 3. Scans migrated tokens with `getAllLoanPositions()` one RPC call per token, positions pre-sorted liquidatable-first
31
+ 3. Scans migrated tokens with `getAllLoanPositions()` -- one RPC call per token, positions pre-sorted liquidatable-first
34
32
  4. Executes `buildLiquidateTransaction()` for each liquidatable position
35
33
  5. Confirms the transaction via `confirmTransaction()`
36
34
  6. Repeats on a configurable interval
@@ -49,15 +47,13 @@ All value flows through the vault. The agent wallet is a stateless controller.
49
47
 
50
48
  ## Vault Setup
51
49
 
52
- The bot uses the torchsdk v3.7.22 vault model:
53
-
54
50
  ```
55
- User (hardware wallet) Creates vault, deposits SOL
56
- Links bot's agent wallet
57
- Bot (disposable) Scans for liquidatable positions
58
- Executes liquidations using vault funds
59
- All proceeds return to vault
60
- User Withdraws from vault (authority only)
51
+ User (hardware wallet) -> Creates vault, deposits SOL
52
+ -> Links bot's agent wallet
53
+ Bot (disposable) -> Scans for liquidatable positions
54
+ -> Executes liquidations using vault funds
55
+ -> All proceeds return to vault
56
+ User -> Withdraws from vault (authority only)
61
57
  ```
62
58
 
63
59
  The agent wallet needs minimal SOL for gas (~0.01 SOL). All liquidation value flows through the vault.
@@ -98,7 +94,7 @@ for (const token of tokens) {
98
94
  borrower: pos.borrower,
99
95
  vault: vaultCreator,
100
96
  })
101
- transaction.sign(agent)
97
+ transaction.sign([agent])
102
98
  const sig = await connection.sendRawTransaction(transaction.serialize())
103
99
  await confirmTransaction(connection, sig, agent.publicKey.toBase58())
104
100
  }
@@ -109,22 +105,12 @@ for (const token of tokens) {
109
105
 
110
106
  ```
111
107
  src/
112
- ├── types.ts BotConfig interface
113
- ├── config.ts loadConfig() (SOLANA_RPC_URL, VAULT_CREATOR, SOLANA_PRIVATE_KEY, SCAN_INTERVAL_MS, LOG_LEVEL)
114
- ├── utils.ts sol(), bpsToPercent(), withTimeout(), createLogger()
115
- └── index.ts vault-based liquidation loop
108
+ ├── types.ts -- BotConfig interface
109
+ ├── config.ts -- loadConfig() (SOLANA_RPC_URL, VAULT_CREATOR, SOLANA_PRIVATE_KEY, SCAN_INTERVAL_MS, LOG_LEVEL)
110
+ ├── utils.ts -- sol(), bpsToPercent(), withTimeout(), createLogger()
111
+ └── index.ts -- vault-based liquidation loop
116
112
  ```
117
113
 
118
- ## Lending Parameters
119
-
120
- | Parameter | Value |
121
- |-----------|-------|
122
- | Max LTV | 50% |
123
- | Liquidation threshold | 65% LTV |
124
- | Interest rate | 2% per epoch (~7 days) |
125
- | Liquidation bonus | 10% of collateral |
126
- | Min borrow | 0.1 SOL |
127
-
128
114
  ## Testing
129
115
 
130
116
  Requires [Surfpool](https://github.com/nicholasgasior/surfpool) running a mainnet fork:
@@ -139,24 +125,11 @@ pnpm test
139
125
  - Agent keypair generated in-process with `Keypair.generate()` (or loaded from optional `SOLANA_PRIVATE_KEY`)
140
126
  - Vault model: agent is a stateless controller, all value stays in the vault
141
127
  - Authority can unlink the agent wallet instantly via `buildUnlinkWalletTransaction()`
142
- - All SDK calls wrapped with 30-second timeout — a hanging RPC cannot stall the bot indefinitely
143
- - Minimal dependencies: `@solana/web3.js` + `torchsdk` -- both pinned to exact versions
128
+ - All SDK calls wrapped with 30-second timeout
129
+ - Minimal dependencies: `@solana/web3.js` + `torchsdk`
144
130
  - No post-install hooks, no remote code fetching
145
131
  - `disable-model-invocation: true` -- agents cannot invoke this skill autonomously
146
132
 
147
- ### External Runtime Dependencies
148
-
149
- The SDK makes outbound HTTPS requests to two external services at runtime (the bot does **not** contact SAID Protocol):
150
-
151
- | Service | Purpose | When Called |
152
- |---------|---------|------------|
153
- | **CoinGecko** (`api.coingecko.com`) | SOL/USD price for display | Token queries via `getTokens()` |
154
- | **Irys Gateway** (`gateway.irys.xyz`) | Token metadata fallback (name, symbol, image) | `getTokens()` when on-chain metadata URI points to Irys |
155
-
156
- `confirmTransaction()` does NOT contact SAID — it only calls `connection.getParsedTransaction()` (Solana RPC). The SDK exports a `verifySaid()` function that contacts `api.saidprotocol.com`, but the bot never calls it.
157
-
158
- No credentials are sent to any external service. All requests are read-only GET. No private key material is ever transmitted.
159
-
160
133
  ## Links
161
134
 
162
135
  - [torchsdk](https://github.com/mrsirg97-rgb/torchsdk) -- the SDK powering this bot