torch-liquidation-bot 2.0.8 → 3.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.
- package/dist/config.d.ts +5 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -6
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -44
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +4 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +15 -6
- package/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/readme.md +88 -37
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* config.ts — loads environment variables into a typed
|
|
2
|
+
* config.ts — loads environment variables into a typed BotConfig.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* the agent keypair is generated in-process. the user never provides a wallet.
|
|
5
|
+
* VAULT_CREATOR identifies which vault the bot operates through.
|
|
5
6
|
*/
|
|
6
|
-
import type {
|
|
7
|
-
export declare
|
|
7
|
+
import type { BotConfig } from './types';
|
|
8
|
+
export declare const loadConfig: () => BotConfig;
|
|
8
9
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,SAAS,CAAA;AAIlD,eAAO,MAAM,UAAU,QAAO,SAkB7B,CAAA"}
|
package/dist/config.js
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* config.ts — loads environment variables into a typed
|
|
3
|
+
* config.ts — loads environment variables into a typed BotConfig.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* the agent keypair is generated in-process. the user never provides a wallet.
|
|
6
|
+
* VAULT_CREATOR identifies which vault the bot operates through.
|
|
6
7
|
*/
|
|
7
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.
|
|
9
|
+
exports.loadConfig = void 0;
|
|
9
10
|
const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
10
|
-
|
|
11
|
+
const loadConfig = () => {
|
|
11
12
|
const rpcUrl = process.env.RPC_URL;
|
|
12
13
|
if (!rpcUrl)
|
|
13
14
|
throw new Error('RPC_URL env var is required');
|
|
15
|
+
const vaultCreator = process.env.VAULT_CREATOR;
|
|
16
|
+
if (!vaultCreator)
|
|
17
|
+
throw new Error('VAULT_CREATOR env var is required (vault creator pubkey)');
|
|
18
|
+
const scanIntervalMs = parseInt(process.env.SCAN_INTERVAL_MS ?? '30000', 10);
|
|
19
|
+
if (isNaN(scanIntervalMs) || scanIntervalMs < 5000) {
|
|
20
|
+
throw new Error('SCAN_INTERVAL_MS must be a number >= 5000');
|
|
21
|
+
}
|
|
14
22
|
const logLevel = (process.env.LOG_LEVEL ?? 'info');
|
|
15
23
|
if (!LOG_LEVELS.includes(logLevel)) {
|
|
16
24
|
throw new Error(`LOG_LEVEL must be one of: ${LOG_LEVELS.join(', ')}`);
|
|
17
25
|
}
|
|
18
|
-
return { rpcUrl, logLevel };
|
|
19
|
-
}
|
|
26
|
+
return { rpcUrl, vaultCreator, scanIntervalMs, logLevel };
|
|
27
|
+
};
|
|
28
|
+
exports.loadConfig = loadConfig;
|
|
20
29
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;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,OAAO,CAAA;IAClC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAE3D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;IAC9C,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAE9F,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,cAAc,EAAE,QAAQ,EAAE,CAAA;AAC3D,CAAC,CAAA;AAlBY,QAAA,UAAU,cAkBtB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* torch-
|
|
3
|
+
* torch-liquidation-bot — vault-based liquidation bot.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* generates an agent keypair in-process. all operations route through
|
|
6
|
+
* a torch vault identified by VAULT_CREATOR. the user never provides a wallet.
|
|
7
7
|
*
|
|
8
8
|
* usage:
|
|
9
|
-
*
|
|
10
|
-
* RPC_URL=<rpc> npx tsx src/index.ts
|
|
9
|
+
* VAULT_CREATOR=<pubkey> RPC_URL=<rpc> npx tsx src/index.ts
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* env:
|
|
12
|
+
* RPC_URL — solana RPC endpoint (required)
|
|
13
|
+
* VAULT_CREATOR — vault creator pubkey (required)
|
|
14
|
+
* SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
|
|
15
|
+
* LOG_LEVEL — debug | info | warn | error (default info)
|
|
14
16
|
*/
|
|
15
17
|
export {};
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* torch-
|
|
4
|
+
* torch-liquidation-bot — vault-based liquidation bot.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* generates an agent keypair in-process. all operations route through
|
|
7
|
+
* a torch vault identified by VAULT_CREATOR. the user never provides a wallet.
|
|
8
8
|
*
|
|
9
9
|
* usage:
|
|
10
|
-
*
|
|
11
|
-
* RPC_URL=<rpc> npx tsx src/index.ts
|
|
10
|
+
* VAULT_CREATOR=<pubkey> RPC_URL=<rpc> npx tsx src/index.ts
|
|
12
11
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* env:
|
|
13
|
+
* RPC_URL — solana RPC endpoint (required)
|
|
14
|
+
* VAULT_CREATOR — vault creator pubkey (required)
|
|
15
|
+
* SCAN_INTERVAL_MS — ms between scan cycles (default 30000, min 5000)
|
|
16
|
+
* LOG_LEVEL — debug | info | warn | error (default info)
|
|
15
17
|
*/
|
|
16
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
19
|
const web3_js_1 = require("@solana/web3.js");
|
|
@@ -19,56 +21,130 @@ const torchsdk_1 = require("torchsdk");
|
|
|
19
21
|
const config_1 = require("./config");
|
|
20
22
|
const utils_1 = require("./utils");
|
|
21
23
|
// ---------------------------------------------------------------------------
|
|
22
|
-
//
|
|
24
|
+
// bootstrap — generate agent keypair in-process
|
|
23
25
|
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.log(`token price: ${(0, utils_1.sol)(token.price_sol)} SOL`);
|
|
30
|
-
console.log(`interest rate: ${(0, utils_1.bpsToPercent)(lending.interest_rate_bps)}`);
|
|
31
|
-
console.log(`max LTV: ${(0, utils_1.bpsToPercent)(lending.max_ltv_bps)}`);
|
|
32
|
-
console.log(`liquidation threshold: ${(0, utils_1.bpsToPercent)(lending.liquidation_threshold_bps)}`);
|
|
33
|
-
console.log(`liquidation bonus: ${(0, utils_1.bpsToPercent)(lending.liquidation_bonus_bps)}`);
|
|
34
|
-
console.log(`treasury SOL avail: ${(0, utils_1.sol)(lending.treasury_sol_available)} SOL`);
|
|
35
|
-
console.log(`total SOL lent: ${lending.total_sol_lent ? (0, utils_1.sol)(lending.total_sol_lent) : (0, utils_1.sol)(0)} SOL`);
|
|
36
|
-
console.log(`active loans: ${lending.active_loans}`);
|
|
37
|
-
}
|
|
38
|
-
async function showAllLending(connection) {
|
|
39
|
-
console.log('=== torch lending monitor ===\n');
|
|
40
|
-
console.log('no MINT specified — showing all migrated tokens with lending\n');
|
|
26
|
+
const agentKeypair = web3_js_1.Keypair.generate();
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// scan & liquidate
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
const scanAndLiquidate = async (connection, log, vaultCreator) => {
|
|
41
31
|
const { tokens } = await (0, torchsdk_1.getTokens)(connection, {
|
|
42
32
|
status: 'migrated',
|
|
43
33
|
sort: 'volume',
|
|
44
|
-
limit:
|
|
34
|
+
limit: 50,
|
|
45
35
|
});
|
|
46
|
-
|
|
36
|
+
log('debug', `discovered ${tokens.length} migrated tokens`);
|
|
37
|
+
for (const token of tokens) {
|
|
38
|
+
let lending;
|
|
39
|
+
try {
|
|
40
|
+
lending = await (0, torchsdk_1.getLendingInfo)(connection, token.mint);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue; // lending not enabled for this token
|
|
44
|
+
}
|
|
45
|
+
if (!lending.active_loans || lending.active_loans === 0)
|
|
46
|
+
continue;
|
|
47
|
+
log('debug', `${token.symbol} — ${lending.active_loans} active loans, ` +
|
|
48
|
+
`threshold: ${(0, utils_1.bpsToPercent)(lending.liquidation_threshold_bps)}, ` +
|
|
49
|
+
`bonus: ${(0, utils_1.bpsToPercent)(lending.liquidation_bonus_bps)}`);
|
|
50
|
+
// get holders as potential borrowers
|
|
51
|
+
let holders;
|
|
47
52
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
`rate: ${(0, utils_1.bpsToPercent)(lending.interest_rate_bps).padEnd(7)} | ` +
|
|
51
|
-
`loans: ${String(lending.active_loans).padEnd(4)} | ` +
|
|
52
|
-
`avail: ${(0, utils_1.sol)(lending.treasury_sol_available)} SOL`);
|
|
53
|
+
const result = await (0, torchsdk_1.getHolders)(connection, token.mint);
|
|
54
|
+
holders = result.holders;
|
|
53
55
|
}
|
|
54
56
|
catch {
|
|
55
|
-
|
|
57
|
+
log('debug', `${token.symbol} — could not fetch holders, skipping`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
for (const holder of holders) {
|
|
61
|
+
let position;
|
|
62
|
+
try {
|
|
63
|
+
position = await (0, torchsdk_1.getLoanPosition)(connection, token.mint, holder.address);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
continue; // no loan position for this holder
|
|
67
|
+
}
|
|
68
|
+
// SDK provides health status directly — skip non-liquidatable positions
|
|
69
|
+
if (position.health !== 'liquidatable')
|
|
70
|
+
continue;
|
|
71
|
+
log('info', `LIQUIDATABLE | ${token.symbol} | borrower=${holder.address.slice(0, 8)}... | ` +
|
|
72
|
+
`LTV=${position.current_ltv_bps != null ? (0, utils_1.bpsToPercent)(position.current_ltv_bps) : '?'} > ` +
|
|
73
|
+
`threshold=${(0, utils_1.bpsToPercent)(lending.liquidation_threshold_bps)} | ` +
|
|
74
|
+
`owed=${(0, utils_1.sol)(position.total_owed)} SOL`);
|
|
75
|
+
// build and execute liquidation through the vault
|
|
76
|
+
try {
|
|
77
|
+
const { transaction, message } = await (0, torchsdk_1.buildLiquidateTransaction)(connection, {
|
|
78
|
+
mint: token.mint,
|
|
79
|
+
liquidator: agentKeypair.publicKey.toBase58(),
|
|
80
|
+
borrower: holder.address,
|
|
81
|
+
vault: vaultCreator,
|
|
82
|
+
});
|
|
83
|
+
transaction.sign(agentKeypair);
|
|
84
|
+
const signature = await connection.sendRawTransaction(transaction.serialize());
|
|
85
|
+
await (0, torchsdk_1.confirmTransaction)(connection, signature, agentKeypair.publicKey.toBase58());
|
|
86
|
+
log('info', `LIQUIDATED | ${token.symbol} | borrower=${holder.address.slice(0, 8)}... | ` +
|
|
87
|
+
`sig=${signature.slice(0, 16)}... | ${message}`);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
log('warn', `LIQUIDATION FAILED | ${token.symbol} | ${holder.address.slice(0, 8)}... | ${err.message}`);
|
|
91
|
+
}
|
|
56
92
|
}
|
|
57
93
|
}
|
|
58
|
-
}
|
|
94
|
+
};
|
|
59
95
|
// ---------------------------------------------------------------------------
|
|
60
|
-
// main —
|
|
96
|
+
// main — vault-routed liquidation loop
|
|
61
97
|
// ---------------------------------------------------------------------------
|
|
62
|
-
async
|
|
63
|
-
const config = (0, config_1.
|
|
98
|
+
const main = async () => {
|
|
99
|
+
const config = (0, config_1.loadConfig)();
|
|
100
|
+
const log = (0, utils_1.createLogger)(config.logLevel);
|
|
64
101
|
const connection = new web3_js_1.Connection(config.rpcUrl, 'confirmed');
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
102
|
+
console.log('=== torch liquidation bot ===');
|
|
103
|
+
console.log(`agent wallet: ${agentKeypair.publicKey.toBase58()}`);
|
|
104
|
+
console.log(`vault creator: ${config.vaultCreator}`);
|
|
105
|
+
console.log(`scan interval: ${config.scanIntervalMs}ms`);
|
|
106
|
+
console.log();
|
|
107
|
+
// verify vault exists
|
|
108
|
+
const vault = await (0, torchsdk_1.getVault)(connection, config.vaultCreator);
|
|
109
|
+
if (!vault) {
|
|
110
|
+
throw new Error(`vault not found for creator ${config.vaultCreator}`);
|
|
111
|
+
}
|
|
112
|
+
log('info', `vault found — authority=${vault.authority}`);
|
|
113
|
+
// verify agent wallet is linked to vault
|
|
114
|
+
const link = await (0, torchsdk_1.getVaultForWallet)(connection, agentKeypair.publicKey.toBase58());
|
|
115
|
+
if (!link) {
|
|
116
|
+
console.log();
|
|
117
|
+
console.log('--- ACTION REQUIRED ---');
|
|
118
|
+
console.log('agent wallet is NOT linked to the vault.');
|
|
119
|
+
console.log('link it by running (from your authority wallet):');
|
|
120
|
+
console.log();
|
|
121
|
+
console.log(` buildLinkWalletTransaction(connection, {`);
|
|
122
|
+
console.log(` authority: "<your-authority-pubkey>",`);
|
|
123
|
+
console.log(` vault_creator: "${config.vaultCreator}",`);
|
|
124
|
+
console.log(` wallet_to_link: "${agentKeypair.publicKey.toBase58()}"`);
|
|
125
|
+
console.log(` })`);
|
|
126
|
+
console.log();
|
|
127
|
+
console.log('then restart the bot.');
|
|
128
|
+
console.log('-----------------------');
|
|
129
|
+
process.exit(1);
|
|
68
130
|
}
|
|
69
|
-
|
|
70
|
-
|
|
131
|
+
log('info', 'agent wallet linked to vault — starting scan loop');
|
|
132
|
+
log('info', `treasury: ${(0, utils_1.sol)(vault.sol_balance ?? 0)} SOL`);
|
|
133
|
+
// scan loop
|
|
134
|
+
while (true) {
|
|
135
|
+
try {
|
|
136
|
+
log('debug', '--- scan cycle start ---');
|
|
137
|
+
await scanAndLiquidate(connection, log, config.vaultCreator);
|
|
138
|
+
log('debug', '--- scan cycle end ---');
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
log('error', `scan cycle error: ${err.message}`);
|
|
142
|
+
}
|
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, config.scanIntervalMs));
|
|
71
144
|
}
|
|
72
|
-
}
|
|
73
|
-
main()
|
|
145
|
+
};
|
|
146
|
+
main().catch((err) => {
|
|
147
|
+
console.error('FATAL:', err.message ?? err);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
74
150
|
//# 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":";;AACA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;;;GAcG;;AAEH,6CAAqD;AACrD,uCAWiB;AACjB,qCAAqC;AACrC,mCAAyD;AAEzD,8EAA8E;AAC9E,gDAAgD;AAChD,8EAA8E;AAE9E,MAAM,YAAY,GAAG,iBAAO,CAAC,QAAQ,EAAE,CAAA;AAEvC,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,gBAAgB,GAAG,KAAK,EAC5B,UAAsB,EACtB,GAAoC,EACpC,YAAoB,EACpB,EAAE;IACF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAA,oBAAS,EAAC,UAAU,EAAE;QAC7C,MAAM,EAAE,UAAU;QAClB,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,EAAE;KACV,CAAC,CAAA;IAEF,GAAG,CAAC,OAAO,EAAE,cAAc,MAAM,CAAC,MAAM,kBAAkB,CAAC,CAAA;IAE3D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,OAAoB,CAAA;QACxB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAA,yBAAc,EAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ,CAAC,qCAAqC;QAChD,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,KAAK,CAAC;YAAE,SAAQ;QAEjE,GAAG,CACD,OAAO,EACP,GAAG,KAAK,CAAC,MAAM,MAAM,OAAO,CAAC,YAAY,iBAAiB;YACxD,cAAc,IAAA,oBAAY,EAAC,OAAO,CAAC,yBAAyB,CAAC,IAAI;YACjE,UAAU,IAAA,oBAAY,EAAC,OAAO,CAAC,qBAAqB,CAAC,EAAE,CAC1D,CAAA;QAED,qCAAqC;QACrC,IAAI,OAA8B,CAAA;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAU,EAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YACvD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,sCAAsC,CAAC,CAAA;YACnE,SAAQ;QACV,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,QAA0B,CAAA;YAC9B,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,IAAA,0BAAe,EAAC,UAAU,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;YAC1E,CAAC;YAAC,MAAM,CAAC;gBACP,SAAQ,CAAC,mCAAmC;YAC9C,CAAC;YAED,wEAAwE;YACxE,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc;gBAAE,SAAQ;YAEhD,GAAG,CACD,MAAM,EACN,kBAAkB,KAAK,CAAC,MAAM,eAAe,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ;gBAC7E,OAAO,QAAQ,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAA,oBAAY,EAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;gBAC3F,aAAa,IAAA,oBAAY,EAAC,OAAO,CAAC,yBAAyB,CAAC,KAAK;gBACjE,QAAQ,IAAA,WAAG,EAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CACzC,CAAA;YAED,kDAAkD;YAClD,IAAI,CAAC;gBACH,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,oCAAyB,EAAC,UAAU,EAAE;oBAC3E,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,UAAU,EAAE,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE;oBAC7C,QAAQ,EAAE,MAAM,CAAC,OAAO;oBACxB,KAAK,EAAE,YAAY;iBACpB,CAAC,CAAA;gBAEF,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;gBAC9B,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,kBAAkB,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,CAAA;gBAC9E,MAAM,IAAA,6BAAkB,EAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAA;gBAElF,GAAG,CACD,MAAM,EACN,gBAAgB,KAAK,CAAC,MAAM,eAAe,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ;oBAC3E,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,OAAO,EAAE,CAClD,CAAA;YACH,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,GAAG,CACD,MAAM,EACN,wBAAwB,KAAK,CAAC,MAAM,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,EAAE,CAC3F,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC,CAAA;AAED,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;IACtB,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAA;IAC3B,MAAM,GAAG,GAAG,IAAA,oBAAY,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACzC,MAAM,UAAU,GAAG,IAAI,oBAAU,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAE7D,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;IAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IACjE,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;IACpD,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,cAAc,IAAI,CAAC,CAAA;IACxD,OAAO,CAAC,GAAG,EAAE,CAAA;IAEb,sBAAsB;IACtB,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAQ,EAAC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;IACvE,CAAC;IACD,GAAG,CAAC,MAAM,EAAE,2BAA2B,KAAK,CAAC,SAAS,EAAE,CAAC,CAAA;IAEzD,yCAAyC;IACzC,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAiB,EAAC,UAAU,EAAE,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAA;IACnF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,EAAE,CAAA;QACb,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;QACtC,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;QACvD,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;QAC/D,OAAO,CAAC,GAAG,EAAE,CAAA;QACb,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;QACzD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAA;QACxD,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,CAAC,YAAY,IAAI,CAAC,CAAA;QAC3D,OAAO,CAAC,GAAG,CAAC,wBAAwB,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;QACzE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACnB,OAAO,CAAC,GAAG,EAAE,CAAA;QACb,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;QACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,GAAG,CAAC,MAAM,EAAE,mDAAmD,CAAC,CAAA;IAChE,GAAG,CAAC,MAAM,EAAE,aAAa,IAAA,WAAG,EAAC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;IAE3D,YAAY;IACZ,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,GAAG,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAA;YACxC,MAAM,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;YAC5D,GAAG,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAA;QACxC,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,OAAO,EAAE,qBAAqB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QAClD,CAAC;QAED,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC,CAAA;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAA;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* types.ts — interfaces for the
|
|
2
|
+
* types.ts — interfaces for the vault-based liquidation bot.
|
|
3
3
|
*/
|
|
4
4
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
5
|
-
export interface
|
|
5
|
+
export interface BotConfig {
|
|
6
6
|
rpcUrl: string;
|
|
7
|
+
vaultCreator: string;
|
|
8
|
+
scanIntervalMs: number;
|
|
7
9
|
logLevel: LogLevel;
|
|
8
10
|
}
|
|
9
11
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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,
|
|
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,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,QAAQ,CAAA;CACnB"}
|
package/dist/types.js
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* utils.ts — shared helpers.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
export declare
|
|
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 function createLogger(minLevel: LogLevel): (level: LogLevel, msg: string) => void;
|
|
6
8
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
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;AAIzE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,IAGzB,OAAO,QAAQ,EAAE,KAAK,MAAM,UAMjD"}
|
package/dist/utils.js
CHANGED
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
* utils.ts — shared helpers.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
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);
|
|
6
10
|
exports.sol = sol;
|
|
11
|
+
const bpsToPercent = (bps) => (bps / 100).toFixed(2) + '%';
|
|
7
12
|
exports.bpsToPercent = bpsToPercent;
|
|
8
|
-
const
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const LEVEL_ORDER = ['debug', 'info', 'warn', 'error'];
|
|
14
|
+
function createLogger(minLevel) {
|
|
15
|
+
const minIdx = LEVEL_ORDER.indexOf(minLevel);
|
|
16
|
+
return function log(level, msg) {
|
|
17
|
+
if (LEVEL_ORDER.indexOf(level) < minIdx)
|
|
18
|
+
return;
|
|
19
|
+
const ts = new Date().toISOString().substr(11, 12);
|
|
20
|
+
const tag = level.toUpperCase().padEnd(5);
|
|
21
|
+
console.log(`[${ts}] ${tag} ${msg}`);
|
|
22
|
+
};
|
|
14
23
|
}
|
|
15
24
|
//# 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":";AAAA;;GAEG
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAWH,oCASC;AAlBD,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,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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torch-liquidation-bot",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
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",
|
|
7
7
|
"bin": {
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc",
|
|
15
15
|
"clean": "rm -rf dist",
|
|
16
|
-
"test": "npx tsx tests/
|
|
16
|
+
"test": "npx tsx tests/test_e2e.ts",
|
|
17
17
|
"format": "prettier --write src/ tests/"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@solana/web3.js": "1.98.4",
|
|
21
|
-
"torchsdk": "2.
|
|
21
|
+
"torchsdk": "3.2.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "20.19.33",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/mrsirg97-rgb/torch-liquidation-
|
|
33
|
+
"url": "https://github.com/mrsirg97-rgb/torch-liquidation-kit"
|
|
34
34
|
},
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"packageManager": "pnpm@9.10.0+sha512.73a29afa36a0d092ece5271de5177ecbf8318d454ecd701343131b8ebc0c1a91c487da46ab77c8e596d6acf1461e3594ced4becedf8921b074fbd8653ed7051c"
|
package/readme.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# torch-liquidation-bot
|
|
1
|
+
# torch-liquidation-bot v3.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
|
-
> **
|
|
5
|
+
> **v3.0.0 Breaking Change:** The bot now operates through the torchsdk v2.1.0 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.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -13,48 +13,98 @@ npm install torch-liquidation-bot
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
#
|
|
17
|
-
RPC_URL=<rpc> npx torch-liquidation-bot
|
|
16
|
+
# 1. start the bot — it prints its agent wallet on startup
|
|
17
|
+
VAULT_CREATOR=<your-vault-creator-pubkey> RPC_URL=<rpc> npx torch-liquidation-bot
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
|
|
19
|
+
# 2. link the printed agent wallet to your vault (one-time, from your authority wallet)
|
|
20
|
+
# the bot will print the exact instructions if the wallet is not yet linked
|
|
21
|
+
|
|
22
|
+
# 3. restart the bot — it will begin scanning and liquidating
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
## What It Does
|
|
24
26
|
|
|
25
|
-
Every migrated token on Torch has a built-in lending market.
|
|
27
|
+
Every migrated token on Torch has a built-in lending market. Borrowers lock tokens as collateral and borrow SOL. When a position's LTV exceeds the liquidation threshold, anyone can liquidate it and earn the liquidation bonus.
|
|
28
|
+
|
|
29
|
+
This bot:
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
1. Generates a disposable `Keypair` in-process (no private key leaves the process)
|
|
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
|
|
38
|
+
|
|
39
|
+
All value flows through the vault. The agent wallet is a stateless controller.
|
|
28
40
|
|
|
29
41
|
## Configuration
|
|
30
42
|
|
|
31
43
|
| Variable | Required | Default | Description |
|
|
32
44
|
|----------|----------|---------|-------------|
|
|
33
|
-
| `RPC_URL` | yes | -- | Solana RPC endpoint
|
|
34
|
-
| `
|
|
45
|
+
| `RPC_URL` | yes | -- | Solana RPC endpoint |
|
|
46
|
+
| `VAULT_CREATOR` | yes | -- | Vault creator pubkey (identifies which vault to use) |
|
|
47
|
+
| `SCAN_INTERVAL_MS` | no | `30000` | Milliseconds between scan cycles (min 5000) |
|
|
35
48
|
| `LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, `error` |
|
|
36
49
|
|
|
37
|
-
|
|
50
|
+
## Vault Setup
|
|
51
|
+
|
|
52
|
+
The bot uses the torchsdk v2.1.0 vault model:
|
|
53
|
+
|
|
54
|
+
```
|
|
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)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The agent wallet needs minimal SOL for gas (~0.01 SOL). All liquidation value flows through the vault.
|
|
38
64
|
|
|
39
65
|
## Programmatic Usage
|
|
40
66
|
|
|
41
67
|
```typescript
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
import { Connection, Keypair } from '@solana/web3.js'
|
|
69
|
+
import {
|
|
70
|
+
getTokens,
|
|
71
|
+
getLendingInfo,
|
|
72
|
+
getHolders,
|
|
73
|
+
getLoanPosition,
|
|
74
|
+
getVault,
|
|
75
|
+
getVaultForWallet,
|
|
76
|
+
buildLiquidateTransaction,
|
|
77
|
+
confirmTransaction,
|
|
78
|
+
} from 'torchsdk'
|
|
79
|
+
|
|
80
|
+
const connection = new Connection('<rpc>', 'confirmed')
|
|
81
|
+
const agent = Keypair.generate()
|
|
82
|
+
|
|
83
|
+
// verify vault and link
|
|
84
|
+
const vault = await getVault(connection, '<vault-creator-pubkey>')
|
|
85
|
+
const link = await getVaultForWallet(connection, agent.publicKey.toBase58())
|
|
86
|
+
|
|
87
|
+
// scan and liquidate
|
|
88
|
+
const { tokens } = await getTokens(connection, { status: 'migrated', sort: 'volume', limit: 50 })
|
|
89
|
+
|
|
90
|
+
for (const token of tokens) {
|
|
91
|
+
const lending = await getLendingInfo(connection, token.mint)
|
|
92
|
+
if (!lending.active_loans) continue
|
|
93
|
+
|
|
94
|
+
const { holders } = await getHolders(connection, token.mint)
|
|
95
|
+
for (const holder of holders) {
|
|
96
|
+
const pos = await getLoanPosition(connection, token.mint, holder.address)
|
|
97
|
+
if (pos.health !== 'liquidatable') continue
|
|
98
|
+
|
|
99
|
+
const { transaction } = await buildLiquidateTransaction(connection, {
|
|
100
|
+
mint: token.mint,
|
|
101
|
+
liquidator: agent.publicKey.toBase58(),
|
|
102
|
+
borrower: holder.address,
|
|
103
|
+
})
|
|
104
|
+
transaction.sign(agent)
|
|
105
|
+
const sig = await connection.sendRawTransaction(transaction.serialize())
|
|
106
|
+
await confirmTransaction(connection, sig, agent.publicKey.toBase58())
|
|
107
|
+
}
|
|
58
108
|
}
|
|
59
109
|
```
|
|
60
110
|
|
|
@@ -62,10 +112,10 @@ for (const t of tokens) {
|
|
|
62
112
|
|
|
63
113
|
```
|
|
64
114
|
src/
|
|
65
|
-
├── types.ts —
|
|
66
|
-
├── config.ts —
|
|
67
|
-
├── utils.ts — sol()
|
|
68
|
-
└── index.ts —
|
|
115
|
+
├── types.ts — BotConfig interface
|
|
116
|
+
├── config.ts — loadConfig() (RPC_URL, VAULT_CREATOR, SCAN_INTERVAL_MS, LOG_LEVEL)
|
|
117
|
+
├── utils.ts — sol(), bpsToPercent(), createLogger()
|
|
118
|
+
└── index.ts — vault-based liquidation loop
|
|
69
119
|
```
|
|
70
120
|
|
|
71
121
|
## Lending Parameters
|
|
@@ -84,21 +134,22 @@ Requires [Surfpool](https://github.com/nicholasgasior/surfpool) running a mainne
|
|
|
84
134
|
|
|
85
135
|
```bash
|
|
86
136
|
surfpool start --network mainnet --no-tui
|
|
87
|
-
pnpm test
|
|
137
|
+
pnpm test
|
|
88
138
|
```
|
|
89
139
|
|
|
90
140
|
## Security
|
|
91
141
|
|
|
92
|
-
-
|
|
93
|
-
- No
|
|
142
|
+
- Agent keypair generated in-process — never serialized, never leaves the process
|
|
143
|
+
- No user wallet or private key imported from environment
|
|
144
|
+
- Vault model: agent is a stateless controller, all value stays in the vault
|
|
145
|
+
- Authority can unlink the agent wallet instantly via `buildUnlinkWalletTransaction()`
|
|
94
146
|
- Outbound connections: Solana RPC only
|
|
95
147
|
- Minimal dependencies: `@solana/web3.js` + `torchsdk`
|
|
96
148
|
- No post-install hooks, no remote code fetching
|
|
97
|
-
- RPC_URL is used only for read-only RPC calls — never logged, transmitted externally, or stored
|
|
98
149
|
|
|
99
150
|
## Links
|
|
100
151
|
|
|
101
|
-
- [torchsdk](https://github.com/mrsirg97-rgb/torchsdk) -- the SDK this
|
|
152
|
+
- [torchsdk](https://github.com/mrsirg97-rgb/torchsdk) -- the SDK powering this bot
|
|
102
153
|
- [Torch Market](https://torch.market) -- the protocol
|
|
103
154
|
- [ClawHub](https://clawhub.ai/mrsirg97-rgb/torchliquidationbot) -- skill registry
|
|
104
155
|
|