torch-liquidation-bot 3.0.2 → 4.0.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.
@@ -0,0 +1,13 @@
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
+ import type { BotConfig } from './types';
12
+ export declare const loadConfig: () => BotConfig;
13
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +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"}
package/dist/config.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * config.ts — loads environment variables into a typed BotConfig.
4
+ *
5
+ * 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)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.loadConfig = void 0;
14
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
15
+ const loadConfig = () => {
16
+ const rpcUrl = process.env.SOLANA_RPC_URL ?? process.env.RPC_URL;
17
+ if (!rpcUrl)
18
+ throw new Error('SOLANA_RPC_URL env var is required (fallback: RPC_URL)');
19
+ const vaultCreator = process.env.VAULT_CREATOR;
20
+ if (!vaultCreator)
21
+ throw new Error('VAULT_CREATOR env var is required (vault creator pubkey)');
22
+ const privateKey = process.env.SOLANA_PRIVATE_KEY ?? null;
23
+ const scanIntervalMs = parseInt(process.env.SCAN_INTERVAL_MS ?? '30000', 10);
24
+ if (isNaN(scanIntervalMs) || scanIntervalMs < 5000) {
25
+ throw new Error('SCAN_INTERVAL_MS must be a number >= 5000');
26
+ }
27
+ 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(', ')}`);
30
+ }
31
+ return { rpcUrl, vaultCreator, privateKey, scanIntervalMs, logLevel };
32
+ };
33
+ exports.loadConfig = loadConfig;
34
+ //# sourceMappingURL=config.js.map
@@ -0,0 +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"}
@@ -0,0 +1,19 @@
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
+ export {};
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
package/dist/index.js ADDED
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
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
+ *
9
+ * 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)
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ const web3_js_1 = require("@solana/web3.js");
21
+ const torchsdk_1 = require("torchsdk");
22
+ const config_1 = require("./config");
23
+ const utils_1 = require("./utils");
24
+ // ---------------------------------------------------------------------------
25
+ // scan & liquidate
26
+ // ---------------------------------------------------------------------------
27
+ const scanAndLiquidate = async (connection, log, vaultCreator, agentKeypair) => {
28
+ const { tokens } = await (0, torchsdk_1.getTokens)(connection, {
29
+ status: 'migrated',
30
+ sort: 'volume',
31
+ limit: 50,
32
+ });
33
+ log('debug', `discovered ${tokens.length} migrated tokens`);
34
+ for (const token of tokens) {
35
+ let positions;
36
+ try {
37
+ const result = await (0, torchsdk_1.getAllLoanPositions)(connection, token.mint);
38
+ positions = result.positions;
39
+ }
40
+ catch {
41
+ continue; // lending not enabled for this token
42
+ }
43
+ if (positions.length === 0)
44
+ continue;
45
+ log('debug', `${token.symbol} — ${positions.length} active loans`);
46
+ // positions are pre-sorted: liquidatable → at_risk → healthy
47
+ for (const position of positions) {
48
+ if (position.health !== 'liquidatable')
49
+ break; // sorted, so no more liquidatable after this
50
+ log('info', `LIQUIDATABLE | ${token.symbol} | borrower=${position.borrower.slice(0, 8)}... | ` +
51
+ `LTV=${position.current_ltv_bps != null ? (0, utils_1.bpsToPercent)(position.current_ltv_bps) : '?'} | ` +
52
+ `owed=${(0, utils_1.sol)(position.total_owed)} SOL`);
53
+ // build and execute liquidation through the vault
54
+ try {
55
+ const { transaction, message } = await (0, torchsdk_1.buildLiquidateTransaction)(connection, {
56
+ mint: token.mint,
57
+ liquidator: agentKeypair.publicKey.toBase58(),
58
+ borrower: position.borrower,
59
+ vault: vaultCreator,
60
+ });
61
+ transaction.sign(agentKeypair);
62
+ const signature = await connection.sendRawTransaction(transaction.serialize());
63
+ await (0, torchsdk_1.confirmTransaction)(connection, signature, agentKeypair.publicKey.toBase58());
64
+ log('info', `LIQUIDATED | ${token.symbol} | borrower=${position.borrower.slice(0, 8)}... | ` +
65
+ `sig=${signature.slice(0, 16)}... | ${message}`);
66
+ }
67
+ catch (err) {
68
+ log('warn', `LIQUIDATION FAILED | ${token.symbol} | ${position.borrower.slice(0, 8)}... | ${err.message}`);
69
+ }
70
+ }
71
+ }
72
+ };
73
+ // ---------------------------------------------------------------------------
74
+ // main — vault-routed liquidation loop
75
+ // ---------------------------------------------------------------------------
76
+ const main = async () => {
77
+ const config = (0, config_1.loadConfig)();
78
+ const log = (0, utils_1.createLogger)(config.logLevel);
79
+ const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
80
+ // load or generate agent keypair
81
+ let agentKeypair;
82
+ if (config.privateKey) {
83
+ try {
84
+ const parsed = JSON.parse(config.privateKey);
85
+ if (Array.isArray(parsed)) {
86
+ agentKeypair = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(parsed));
87
+ }
88
+ else {
89
+ throw new Error('SOLANA_PRIVATE_KEY JSON must be a byte array');
90
+ }
91
+ }
92
+ catch (e) {
93
+ if (e.message?.includes('byte array'))
94
+ throw e;
95
+ // not JSON — try base58
96
+ agentKeypair = web3_js_1.Keypair.fromSecretKey((0, utils_1.decodeBase58)(config.privateKey));
97
+ }
98
+ log('info', 'loaded keypair from SOLANA_PRIVATE_KEY');
99
+ }
100
+ else {
101
+ agentKeypair = web3_js_1.Keypair.generate();
102
+ log('info', 'generated fresh agent keypair');
103
+ }
104
+ console.log('=== torch liquidation bot ===');
105
+ console.log(`agent wallet: ${agentKeypair.publicKey.toBase58()}`);
106
+ console.log(`vault creator: ${config.vaultCreator}`);
107
+ console.log(`scan interval: ${config.scanIntervalMs}ms`);
108
+ console.log();
109
+ // verify vault exists
110
+ const vault = await (0, torchsdk_1.getVault)(connection, config.vaultCreator);
111
+ if (!vault) {
112
+ throw new Error(`vault not found for creator ${config.vaultCreator}`);
113
+ }
114
+ log('info', `vault found — authority=${vault.authority}`);
115
+ // verify agent wallet is linked to vault
116
+ const link = await (0, torchsdk_1.getVaultForWallet)(connection, agentKeypair.publicKey.toBase58());
117
+ if (!link) {
118
+ console.log();
119
+ console.log('--- ACTION REQUIRED ---');
120
+ console.log('agent wallet is NOT linked to the vault.');
121
+ console.log('link it by running (from your authority wallet):');
122
+ console.log();
123
+ console.log(` buildLinkWalletTransaction(connection, {`);
124
+ console.log(` authority: "<your-authority-pubkey>",`);
125
+ console.log(` vault_creator: "${config.vaultCreator}",`);
126
+ console.log(` wallet_to_link: "${agentKeypair.publicKey.toBase58()}"`);
127
+ console.log(` })`);
128
+ console.log();
129
+ console.log('then restart the bot.');
130
+ console.log('-----------------------');
131
+ process.exit(1);
132
+ }
133
+ log('info', 'agent wallet linked to vault — starting scan loop');
134
+ log('info', `treasury: ${(0, utils_1.sol)(vault.sol_balance ?? 0)} SOL`);
135
+ // scan loop
136
+ while (true) {
137
+ try {
138
+ log('debug', '--- scan cycle start ---');
139
+ await scanAndLiquidate(connection, log, config.vaultCreator, agentKeypair);
140
+ log('debug', '--- scan cycle end ---');
141
+ }
142
+ catch (err) {
143
+ log('error', `scan cycle error: ${err.message}`);
144
+ }
145
+ await new Promise((resolve) => setTimeout(resolve, config.scanIntervalMs));
146
+ }
147
+ };
148
+ main().catch((err) => {
149
+ console.error('FATAL:', err.message ?? err);
150
+ process.exit(1);
151
+ });
152
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * types.ts — interfaces for the vault-based liquidation bot.
3
+ */
4
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
5
+ export interface BotConfig {
6
+ rpcUrl: string;
7
+ vaultCreator: string;
8
+ privateKey: string | null;
9
+ scanIntervalMs: number;
10
+ logLevel: LogLevel;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * types.ts — interfaces for the vault-based liquidation bot.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * utils.ts — shared helpers.
3
+ */
4
+ import type { LogLevel } from './types';
5
+ export declare const sol: (lamports: number) => string;
6
+ export declare const bpsToPercent: (bps: number) => string;
7
+ export declare const decodeBase58: (s: string) => Uint8Array;
8
+ export declare function createLogger(minLevel: LogLevel): (level: LogLevel, msg: string) => void;
9
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +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,YAAY,CAAC,QAAQ,EAAE,QAAQ,IAGzB,OAAO,QAAQ,EAAE,KAAK,MAAM,UAMjD"}
package/dist/utils.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * utils.ts — shared helpers.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.decodeBase58 = exports.bpsToPercent = exports.sol = void 0;
7
+ exports.createLogger = createLogger;
8
+ const torchsdk_1 = require("torchsdk");
9
+ const sol = (lamports) => (lamports / torchsdk_1.LAMPORTS_PER_SOL).toFixed(4);
10
+ exports.sol = sol;
11
+ const bpsToPercent = (bps) => (bps / 100).toFixed(2) + '%';
12
+ exports.bpsToPercent = bpsToPercent;
13
+ // base58 decoder — avoids ESM-only bs58 dependency
14
+ const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
15
+ const decodeBase58 = (s) => {
16
+ const result = [];
17
+ for (let i = 0; i < s.length; i++) {
18
+ let carry = B58.indexOf(s[i]);
19
+ if (carry < 0)
20
+ throw new Error(`invalid base58 character: ${s[i]}`);
21
+ for (let j = 0; j < result.length; j++) {
22
+ carry += result[j] * 58;
23
+ result[j] = carry & 0xff;
24
+ carry >>= 8;
25
+ }
26
+ while (carry > 0) {
27
+ result.push(carry & 0xff);
28
+ carry >>= 8;
29
+ }
30
+ }
31
+ for (let i = 0; i < s.length && s[i] === '1'; i++) {
32
+ result.push(0);
33
+ }
34
+ return new Uint8Array(result.reverse());
35
+ };
36
+ exports.decodeBase58 = decodeBase58;
37
+ const LEVEL_ORDER = ['debug', 'info', 'warn', 'error'];
38
+ function createLogger(minLevel) {
39
+ const minIdx = LEVEL_ORDER.indexOf(minLevel);
40
+ return function log(level, msg) {
41
+ if (LEVEL_ORDER.indexOf(level) < minIdx)
42
+ return;
43
+ const ts = new Date().toISOString().substr(11, 12);
44
+ const tag = level.toUpperCase().padEnd(5);
45
+ console.log(`[${ts}] ${tag} ${msg}`);
46
+ };
47
+ }
48
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAmCH,oCASC;AA1CD,uCAA2C;AAGpC,MAAM,GAAG,GAAG,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,QAAQ,GAAG,2BAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAApE,QAAA,GAAG,OAAiE;AAE1E,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;AAA5D,QAAA,YAAY,gBAAgD;AAEzE,mDAAmD;AACnD,MAAM,GAAG,GAAG,4DAA4D,CAAA;AAEjE,MAAM,YAAY,GAAG,CAAC,CAAS,EAAc,EAAE;IACpD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7B,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACnE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;YACvB,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAA;YACxB,KAAK,KAAK,CAAC,CAAA;QACb,CAAC;QACD,OAAO,KAAK,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;YACzB,KAAK,KAAK,CAAC,CAAA;QACb,CAAC;IACH,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAChB,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;AACzC,CAAC,CAAA;AAnBY,QAAA,YAAY,gBAmBxB;AAED,MAAM,WAAW,GAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AAElE,SAAgB,YAAY,CAAC,QAAkB;IAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE5C,OAAO,SAAS,GAAG,CAAC,KAAe,EAAE,GAAW;QAC9C,IAAI,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM;YAAE,OAAM;QAC/C,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACzC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC,CAAA;IACtC,CAAC,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torch-liquidation-bot",
3
- "version": "3.0.2",
3
+ "version": "4.0.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",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@solana/web3.js": "1.98.4",
21
- "torchsdk": "3.2.3"
21
+ "torchsdk": "3.7.22"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "20.19.33",
@@ -28,7 +28,7 @@
28
28
  "peerDependencies": {
29
29
  "@solana/web3.js": "^1.98.0"
30
30
  },
31
- "repository": {
31
+ "repository": {
32
32
  "type": "git",
33
33
  "url": "https://github.com/mrsirg97-rgb/torch-liquidation-kit"
34
34
  },
package/readme.md CHANGED
@@ -1,13 +1,13 @@
1
- # torch-liquidation-bot v3.0.2 (Vault Mode)
1
+ # torch-liquidation-bot v4.0.0 (Vault Mode)
2
2
 
3
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
4
 
5
- > **v3.0.0+ Breaking Change:** The bot now operates through the torchsdk v3.2.3+ vault model. It generates a disposable agent keypair at startup, scans for underwater loan positions, and executes liquidations. The user never provides a wallet — only a vault creator pubkey and an RPC endpoint.
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.
6
6
 
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install torch-liquidation-bot
10
+ npm install torch-liquidation-bot@4.0.0
11
11
  ```
12
12
 
13
13
  ## Quick Start
@@ -30,11 +30,10 @@ This bot:
30
30
 
31
31
  1. Generates a disposable `Keypair` in-process (no private key leaves the process)
32
32
  2. Verifies the vault exists and the agent wallet is linked
33
- 3. Scans migrated tokens for active loans
34
- 4. Checks each borrower's position health via `getLoanPosition()`
35
- 5. Executes `buildLiquidateTransaction()` for any position with `health === 'liquidatable'`
36
- 6. Confirms the transaction via `confirmTransaction()`
37
- 7. Repeats on a configurable interval
33
+ 3. Scans migrated tokens with `getAllLoanPositions()` — one RPC call per token, positions pre-sorted liquidatable-first
34
+ 4. Executes `buildLiquidateTransaction()` for each liquidatable position
35
+ 5. Confirms the transaction via `confirmTransaction()`
36
+ 6. Repeats on a configurable interval
38
37
 
39
38
  All value flows through the vault. The agent wallet is a stateless controller.
40
39
 
@@ -50,7 +49,7 @@ All value flows through the vault. The agent wallet is a stateless controller.
50
49
 
51
50
  ## Vault Setup
52
51
 
53
- The bot uses the torchsdk v3.2.3 vault model:
52
+ The bot uses the torchsdk v3.7.22 vault model:
54
53
 
55
54
  ```
56
55
  User (hardware wallet) → Creates vault, deposits SOL
@@ -69,9 +68,7 @@ The agent wallet needs minimal SOL for gas (~0.01 SOL). All liquidation value fl
69
68
  import { Connection, Keypair } from '@solana/web3.js'
70
69
  import {
71
70
  getTokens,
72
- getLendingInfo,
73
- getHolders,
74
- getLoanPosition,
71
+ getAllLoanPositions,
75
72
  getVault,
76
73
  getVaultForWallet,
77
74
  buildLiquidateTransaction,
@@ -90,18 +87,15 @@ const link = await getVaultForWallet(connection, agent.publicKey.toBase58())
90
87
  const { tokens } = await getTokens(connection, { status: 'migrated', sort: 'volume', limit: 50 })
91
88
 
92
89
  for (const token of tokens) {
93
- const lending = await getLendingInfo(connection, token.mint)
94
- if (!lending.active_loans) continue
90
+ const { positions } = await getAllLoanPositions(connection, token.mint)
95
91
 
96
- const { holders } = await getHolders(connection, token.mint)
97
- for (const holder of holders) {
98
- const pos = await getLoanPosition(connection, token.mint, holder.address)
99
- if (pos.health !== 'liquidatable') continue
92
+ for (const pos of positions) {
93
+ if (pos.health !== 'liquidatable') break // pre-sorted, done
100
94
 
101
95
  const { transaction } = await buildLiquidateTransaction(connection, {
102
96
  mint: token.mint,
103
97
  liquidator: agent.publicKey.toBase58(),
104
- borrower: holder.address,
98
+ borrower: pos.borrower,
105
99
  vault: vaultCreator,
106
100
  })
107
101
  transaction.sign(agent)