torch-liquidation-bot 10.5.0 → 10.5.2
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/package.json +1 -1
- package/readme.md +67 -36
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# torch-liquidation-bot
|
|
2
2
|
|
|
3
|
-
Vault-based liquidation keeper for [Torch Market](https://torch.market) on Solana. Generates an agent keypair in-process
|
|
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. Built on [torchsdk](https://www.npmjs.com/package/torchsdk) v10.5.0.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -11,55 +11,71 @@ npm install torch-liquidation-bot
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
# 1. start the bot
|
|
14
|
+
# 1. start the bot — it prints its agent wallet on startup
|
|
15
15
|
VAULT_CREATOR=<your-vault-creator-pubkey> SOLANA_RPC_URL=<rpc> npx torch-liquidation-bot
|
|
16
16
|
|
|
17
17
|
# 2. link the printed agent wallet to your vault (one-time, from your authority wallet)
|
|
18
|
-
# the bot
|
|
18
|
+
# the bot prints the exact instructions if the wallet is not yet linked
|
|
19
19
|
|
|
20
|
-
# 3. restart the bot
|
|
20
|
+
# 3. restart the bot — it will begin scanning and liquidating
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## What It Does
|
|
24
24
|
|
|
25
|
-
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
|
|
25
|
+
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 65% liquidation threshold, anyone can liquidate it and collect a 10% collateral bonus.
|
|
26
26
|
|
|
27
27
|
This bot:
|
|
28
28
|
|
|
29
|
-
1. Generates a disposable `Keypair` in-process (no private key leaves the process)
|
|
29
|
+
1. Generates a disposable `Keypair` in-process (no private key leaves the process; optional `SOLANA_PRIVATE_KEY` overrides)
|
|
30
30
|
2. Verifies the vault exists and the agent wallet is linked
|
|
31
|
-
3. Scans migrated tokens with `getAllLoanPositions()`
|
|
32
|
-
4. Executes `buildLiquidateTransaction()` for each liquidatable position
|
|
33
|
-
5. Confirms
|
|
31
|
+
3. Scans migrated tokens with `getAllLoanPositions()` — one RPC call per token, positions pre-sorted liquidatable-first
|
|
32
|
+
4. Executes `buildLiquidateTransaction()` for each liquidatable position, routing bonus tokens into the vault
|
|
33
|
+
5. Confirms via `confirmTransaction()` and records metrics
|
|
34
34
|
6. Repeats on a configurable interval
|
|
35
35
|
|
|
36
|
-
All value flows through the vault. The agent wallet is a stateless controller.
|
|
36
|
+
All value flows through the vault. The agent wallet is a stateless controller that holds only gas SOL.
|
|
37
|
+
|
|
38
|
+
**Off-chain health visibility (torchsdk v10.5.0+):** the SDK projects accrued interest forward to the current slot, so positions that have drifted past the liquidation threshold via interest accrual alone — without any on-chain instruction touching them — show up as `health=liquidatable` immediately. No need for someone else to poke the loan first.
|
|
37
39
|
|
|
38
40
|
## Configuration
|
|
39
41
|
|
|
40
42
|
| Variable | Required | Default | Description |
|
|
41
43
|
|----------|----------|---------|-------------|
|
|
42
|
-
| `SOLANA_RPC_URL` | yes |
|
|
43
|
-
| `VAULT_CREATOR` | yes |
|
|
44
|
-
| `SOLANA_PRIVATE_KEY` | no |
|
|
44
|
+
| `SOLANA_RPC_URL` | yes | — | Solana RPC endpoint (fallback: `RPC_URL`) |
|
|
45
|
+
| `VAULT_CREATOR` | yes | — | Vault creator pubkey (identifies which vault to use) |
|
|
46
|
+
| `SOLANA_PRIVATE_KEY` | no | — | Disposable controller keypair (base58 or JSON byte array). If omitted, generates fresh keypair on startup |
|
|
45
47
|
| `SCAN_INTERVAL_MS` | no | `30000` | Milliseconds between scan cycles (min 5000) |
|
|
48
|
+
| `SCAN_LIMIT` | no | `50` | Max tokens scanned per cycle (`0` = unlimited) |
|
|
49
|
+
| `MIN_AGENT_BALANCE_SOL` | no | `0.01` | Pause liquidations when agent's gas balance drops below this |
|
|
46
50
|
| `LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, `error` |
|
|
51
|
+
| `LOG_FORMAT` | no | `text` | `text` (human-readable) or `json` (structured, one record per line) |
|
|
47
52
|
|
|
48
53
|
## Vault Setup
|
|
49
54
|
|
|
50
55
|
```
|
|
51
|
-
User (hardware wallet)
|
|
52
|
-
|
|
53
|
-
Bot (disposable)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
User
|
|
56
|
+
User (hardware wallet) → creates vault, deposits SOL
|
|
57
|
+
→ links bot's agent wallet
|
|
58
|
+
Bot (disposable) → scans for liquidatable positions
|
|
59
|
+
→ executes liquidations using vault funds
|
|
60
|
+
→ all proceeds return to vault
|
|
61
|
+
User → withdraws from vault (authority only)
|
|
57
62
|
```
|
|
58
63
|
|
|
59
|
-
The agent wallet needs minimal SOL for gas (~0.01 SOL). All liquidation value flows through the vault.
|
|
64
|
+
The agent wallet needs minimal SOL for gas (~0.01 SOL default, configurable via `MIN_AGENT_BALANCE_SOL`). All liquidation value flows through the vault. If the agent balance drops below the threshold, the bot pauses that cycle and logs a warning rather than failing transactions mid-flight.
|
|
65
|
+
|
|
66
|
+
## Operational Features
|
|
67
|
+
|
|
68
|
+
- **Graceful shutdown.** `SIGINT` / `SIGTERM` abort the current scan cleanly — the bot finishes the in-flight RPC call, skips remaining tokens in the cycle, and exits with `graceful shutdown complete` + code 0. Safe for `systemctl stop`, container orchestrators, etc.
|
|
69
|
+
- **Retry with exponential backoff.** Every RPC call is wrapped in a 3-attempt retry with 1s / 2s / 4s delays and tracked via `stats.rpcRetries`. Transient Solana RPC failures don't kill a scan cycle.
|
|
70
|
+
- **30s timeout on every SDK call.** A hung RPC can't wedge the bot; the timeout throws and the retry logic kicks in.
|
|
71
|
+
- **Balance-pause check.** Pre-flight check before each scan cycle — if the agent's balance dips below `MIN_AGENT_BALANCE_SOL`, the cycle is skipped with an error log.
|
|
72
|
+
- **Structured JSON logging.** `LOG_FORMAT=json` emits one JSON record per line — pipe straight into Vector, Datadog, Loki, etc.
|
|
73
|
+
- **Runtime stats.** Every cycle logs cumulative counters: `cycles`, `liquidations`, `failures`, `rpc_retries`, `uptime_sec`, `lastError`.
|
|
60
74
|
|
|
61
75
|
## Programmatic Usage
|
|
62
76
|
|
|
77
|
+
If you want the scanning loop embedded in your own service instead of running the bot binary:
|
|
78
|
+
|
|
63
79
|
```typescript
|
|
64
80
|
import { Connection, Keypair } from '@solana/web3.js'
|
|
65
81
|
import {
|
|
@@ -74,19 +90,28 @@ import {
|
|
|
74
90
|
const connection = new Connection('<rpc>', 'confirmed')
|
|
75
91
|
const agent = Keypair.generate()
|
|
76
92
|
|
|
77
|
-
// verify vault and link
|
|
93
|
+
// verify vault and link (one-time)
|
|
78
94
|
const vaultCreator = '<vault-creator-pubkey>'
|
|
79
95
|
const vault = await getVault(connection, vaultCreator)
|
|
96
|
+
if (!vault) throw new Error('vault not found')
|
|
97
|
+
|
|
80
98
|
const link = await getVaultForWallet(connection, agent.publicKey.toBase58())
|
|
99
|
+
if (!link) throw new Error('agent wallet not linked to vault')
|
|
81
100
|
|
|
82
101
|
// scan and liquidate
|
|
83
|
-
const { tokens } = await getTokens(connection, {
|
|
102
|
+
const { tokens } = await getTokens(connection, {
|
|
103
|
+
status: 'migrated',
|
|
104
|
+
sort: 'volume',
|
|
105
|
+
limit: 50,
|
|
106
|
+
})
|
|
84
107
|
|
|
85
108
|
for (const token of tokens) {
|
|
86
109
|
const { positions } = await getAllLoanPositions(connection, token.mint)
|
|
87
110
|
|
|
88
111
|
for (const pos of positions) {
|
|
89
|
-
|
|
112
|
+
// positions are pre-sorted liquidatable → at_risk → healthy.
|
|
113
|
+
// health is already projected to the current slot — no need to call accrue_interest first.
|
|
114
|
+
if (pos.health !== 'liquidatable') break
|
|
90
115
|
|
|
91
116
|
const { transaction } = await buildLiquidateTransaction(connection, {
|
|
92
117
|
mint: token.mint,
|
|
@@ -104,37 +129,43 @@ for (const token of tokens) {
|
|
|
104
129
|
## Architecture
|
|
105
130
|
|
|
106
131
|
```
|
|
107
|
-
src/
|
|
108
|
-
├──
|
|
109
|
-
├──
|
|
110
|
-
├──
|
|
111
|
-
|
|
132
|
+
packages/bot/src/
|
|
133
|
+
├── constants.ts — retry/timeout constants, log level/format tables
|
|
134
|
+
├── types.ts — BotConfig, BotStats, ScanContext, Logger, LogLevel, LogFormat
|
|
135
|
+
├── config.ts — loadConfig() + env-var validation
|
|
136
|
+
├── utils.ts — withTimeout, withRetry, createLogger, sol/bpsToPercent formatters
|
|
137
|
+
└── index.ts — scanAndLiquidate + main() with graceful shutdown
|
|
112
138
|
```
|
|
113
139
|
|
|
114
140
|
## Testing
|
|
115
141
|
|
|
116
|
-
Requires [Surfpool](https://github.com/
|
|
142
|
+
Requires [Surfpool](https://github.com/txtx/surfpool) running a mainnet fork:
|
|
117
143
|
|
|
118
144
|
```bash
|
|
119
145
|
surfpool start --network mainnet --no-tui
|
|
146
|
+
pnpm build
|
|
120
147
|
pnpm test
|
|
121
148
|
```
|
|
122
149
|
|
|
150
|
+
The e2e covers the full flow: create token → bond → migrate → open loan → time-travel past threshold → scan → liquidate via vault → verify cleanup → balance-pause check → config validation → subprocess SIGTERM shutdown.
|
|
151
|
+
|
|
123
152
|
## Security
|
|
124
153
|
|
|
125
154
|
- Agent keypair generated in-process with `Keypair.generate()` (or loaded from optional `SOLANA_PRIVATE_KEY`)
|
|
126
|
-
- Vault model: agent is a stateless controller
|
|
155
|
+
- Vault model: agent is a stateless controller; all value stays in the vault
|
|
127
156
|
- Authority can unlink the agent wallet instantly via `buildUnlinkWalletTransaction()`
|
|
128
|
-
- All SDK calls wrapped with 30-second timeout
|
|
129
|
-
-
|
|
157
|
+
- All SDK calls wrapped with a 30-second timeout + 3-attempt retry
|
|
158
|
+
- Pre-flight balance check pauses liquidations before funds run out for gas
|
|
159
|
+
- Minimal dependencies: `@solana/web3.js`, `torchsdk`, `bs58`, `@solana/spl-token`
|
|
130
160
|
- No post-install hooks, no remote code fetching
|
|
131
|
-
- `disable-model-invocation: true`
|
|
161
|
+
- `disable-model-invocation: true` — agents cannot invoke this skill autonomously
|
|
132
162
|
|
|
133
163
|
## Links
|
|
134
164
|
|
|
135
|
-
- [torchsdk](https://github.com/mrsirg97-rgb/torchsdk)
|
|
136
|
-
- [Torch Market](https://torch.market)
|
|
137
|
-
- [ClawHub](https://clawhub.ai/mrsirg97-rgb/torch-liquidation-bot)
|
|
165
|
+
- [torchsdk](https://github.com/mrsirg97-rgb/torchsdk) — the SDK powering this bot
|
|
166
|
+
- [Torch Market](https://torch.market) — the protocol
|
|
167
|
+
- [ClawHub](https://clawhub.ai/mrsirg97-rgb/torch-liquidation-bot) — skill registry
|
|
168
|
+
- program id: `8hbUkonssSEEtkqzwM7ZcZrD9evacM92TcWSooVF4BeT`
|
|
138
169
|
|
|
139
170
|
## License
|
|
140
171
|
|