litcoin-mcp 1.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/README.md +88 -0
- package/package.json +19 -0
- package/src/index.js +656 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# LITCOIN MCP Server
|
|
2
|
+
|
|
3
|
+
MCP server for the [LITCOIN](https://litcoiin.xyz) proof-of-comprehension protocol on Base. Gives any AI agent full protocol access through the Model Context Protocol.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### Claude Desktop / Claude Code
|
|
8
|
+
|
|
9
|
+
Add to your MCP config (`claude_desktop_config.json` or `.claude.json`):
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"litcoin": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "litcoin-mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"BANKR_API_KEY": "bk_YOUR_KEY"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Cursor
|
|
26
|
+
|
|
27
|
+
Add to `.cursor/mcp.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"litcoin": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "litcoin-mcp"],
|
|
35
|
+
"env": {
|
|
36
|
+
"BANKR_API_KEY": "bk_YOUR_KEY"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Get a Bankr API key at [bankr.bot/api](https://bankr.bot/api).
|
|
44
|
+
|
|
45
|
+
## Available Tools
|
|
46
|
+
|
|
47
|
+
| Tool | Description |
|
|
48
|
+
|------|-------------|
|
|
49
|
+
| `litcoin_balance` | Check LITCOIN, LITCREDIT, staking tier, mining boost, escrow balance |
|
|
50
|
+
| `litcoin_network` | Network stats: active miners, treasury, rewards, oracle prices |
|
|
51
|
+
| `litcoin_mine` | Mine one round (solve a comprehension challenge, earn LITCOIN) |
|
|
52
|
+
| `litcoin_claim` | Claim accumulated mining rewards on-chain |
|
|
53
|
+
| `litcoin_claimable` | Check how much is available to claim |
|
|
54
|
+
| `litcoin_stake` | Stake into a tier (1=Spark, 2=Circuit, 3=Core, 4=Architect) |
|
|
55
|
+
| `litcoin_unstake` | Unstake after lock period |
|
|
56
|
+
| `litcoin_open_vault` | Open a vault with LITCOIN collateral |
|
|
57
|
+
| `litcoin_mint` | Mint LITCREDIT against a vault |
|
|
58
|
+
| `litcoin_repay` | Repay LITCREDIT debt |
|
|
59
|
+
| `litcoin_add_collateral` | Add collateral to a vault |
|
|
60
|
+
| `litcoin_close_vault` | Close a vault |
|
|
61
|
+
| `litcoin_vaults` | List all vaults with health ratios |
|
|
62
|
+
| `litcoin_deposit_escrow` | Deposit LITCREDIT for compute spending |
|
|
63
|
+
| `litcoin_compute` | Send a prompt to the compute marketplace |
|
|
64
|
+
| `litcoin_create_guild` | Create a mining guild |
|
|
65
|
+
| `litcoin_join_guild` | Join a guild with deposit |
|
|
66
|
+
| `litcoin_leave_guild` | Leave a guild |
|
|
67
|
+
| `litcoin_faucet` | Claim 5M free LITCOIN (one-time bootstrap) |
|
|
68
|
+
|
|
69
|
+
## Example Conversation
|
|
70
|
+
|
|
71
|
+
> **You:** Check my LITCOIN balance
|
|
72
|
+
>
|
|
73
|
+
> **Agent:** *(calls litcoin_balance)* You have 3,200,000 LITCOIN and 0 LITCREDIT. You're not staked yet.
|
|
74
|
+
>
|
|
75
|
+
> **You:** Stake into Circuit tier
|
|
76
|
+
>
|
|
77
|
+
> **Agent:** *(calls litcoin_stake with tier=2)* Done! Staked 5M LITCOIN into Circuit tier. Mining boost: 1.25x.
|
|
78
|
+
>
|
|
79
|
+
> **You:** Mine 5 rounds
|
|
80
|
+
>
|
|
81
|
+
> **Agent:** *(calls litcoin_mine 5 times)* Mined 5 rounds. Earned 750,000 LITCOIN total.
|
|
82
|
+
|
|
83
|
+
## Links
|
|
84
|
+
|
|
85
|
+
- [LITCOIN Website](https://litcoiin.xyz)
|
|
86
|
+
- [Full Docs (AI-readable)](https://litcoiin.xyz/docs.md)
|
|
87
|
+
- [SDK on PyPI](https://pypi.org/project/litcoin/)
|
|
88
|
+
- [Twitter](https://x.com/litcoin_AI)
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "litcoin-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for the LITCOIN proof-of-comprehension protocol on Base. Mine, stake, vault, compute — all from any AI agent.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"litcoin-mcp": "src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["mcp", "litcoin", "mining", "crypto", "ai-agent", "base", "defi"],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
17
|
+
"zod": "^3.25.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LITCOIN MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Exposes the full LITCOIN protocol to any MCP-compatible AI agent.
|
|
7
|
+
* Mine, claim, stake, vault, compute, guilds — all through tool calls.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* BANKR_API_KEY=bk_... npx litcoin-mcp
|
|
11
|
+
*
|
|
12
|
+
* Claude Desktop / Claude Code config:
|
|
13
|
+
* {
|
|
14
|
+
* "mcpServers": {
|
|
15
|
+
* "litcoin": {
|
|
16
|
+
* "command": "npx",
|
|
17
|
+
* "args": ["litcoin-mcp"],
|
|
18
|
+
* "env": { "BANKR_API_KEY": "bk_YOUR_KEY" }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const BANKR_API_KEY = process.env.BANKR_API_KEY || "";
|
|
31
|
+
const COORDINATOR = process.env.COORDINATOR_URL || "https://api.litcoiin.xyz";
|
|
32
|
+
const BANKR_BASE = "https://api.bankr.bot";
|
|
33
|
+
|
|
34
|
+
const CONTRACTS = {
|
|
35
|
+
LITCOIN: "0x316ffb9c875f900AdCF04889E415cC86b564EBa3",
|
|
36
|
+
STAKING: "0xC9584Ce1591E8EB38EdF15C28f2FDcca97A3d3B7",
|
|
37
|
+
ORACLE: "0x4f937937A3B7Ca046d0f2B5071782aFFC675241b",
|
|
38
|
+
LITCREDIT: "0x33e3d328F62037EB0d173705674CE713c348f0a6",
|
|
39
|
+
VAULT_MANAGER: "0xD23a9b32e38FABE2325e1d27f94EcCf0e4a2f058",
|
|
40
|
+
CLAIMS: "0xF703DcF2E88C0673F776870fdb12A453927C6A5e",
|
|
41
|
+
COMPUTE_ESCROW: "0x28C351FE1A37434DD63882dA51b5f4CBade71724",
|
|
42
|
+
GUILD: "0xC377cbD6739678E0fae16e52970755f50AF55bD1",
|
|
43
|
+
FAUCET: "0x1659875dE16090c84C81DF1BDba3c3B4df093557",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const SELECTORS = {
|
|
47
|
+
balanceOf: "0x70a08231",
|
|
48
|
+
totalStaked: "0x817b1cd2",
|
|
49
|
+
getTier: "0xb45aae52",
|
|
50
|
+
getStakeInfo: "0xc3453153",
|
|
51
|
+
getMiningBoostBps: "0x1e9afdb1",
|
|
52
|
+
timeUntilUnlock: "0xd0228e8f",
|
|
53
|
+
getOwnerVaultIds: "0xa6f83a09",
|
|
54
|
+
getVaultCollateralRatio:"0x31ef9daa",
|
|
55
|
+
getMaxMintable: "0x0725f4cb",
|
|
56
|
+
isLiquidatable: "0x211a4443",
|
|
57
|
+
cpiPrice: "0x3ad868db",
|
|
58
|
+
litcoinPrice: "0x1cb870fc",
|
|
59
|
+
available: "0x10098ad5",
|
|
60
|
+
stake: "0x604f2177",
|
|
61
|
+
unstake: "0x2def6620",
|
|
62
|
+
upgradeTier: "0xa2949dd7",
|
|
63
|
+
openVault: "0x04a2ce7e",
|
|
64
|
+
addCollateral: "0xa8f35adf",
|
|
65
|
+
mintLitCredit: "0x91b4ffb4",
|
|
66
|
+
repayDebt: "0x015cb0a5",
|
|
67
|
+
withdrawCollateral: "0x767a7b05",
|
|
68
|
+
closeVault: "0x360c3288",
|
|
69
|
+
deposit: "0xb6b55f25",
|
|
70
|
+
requestWithdraw: "0x745400c9",
|
|
71
|
+
cancelWithdraw: "0x84b76824",
|
|
72
|
+
completeWithdraw: "0xf756fa21",
|
|
73
|
+
approve: "0x095ea7b3",
|
|
74
|
+
createGuild: "0x1c0e61dc",
|
|
75
|
+
joinGuild: "0xa3e77e2a",
|
|
76
|
+
addDeposit: "0xa6f1fd51",
|
|
77
|
+
leaveGuild: "0x0d3dfa92",
|
|
78
|
+
stakeGuild: "0x3deb2bb7",
|
|
79
|
+
unstakeGuild: "0x7b5f7292",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const RPC_URLS = [
|
|
83
|
+
"https://base.llamarpc.com",
|
|
84
|
+
"https://base-rpc.publicnode.com",
|
|
85
|
+
"https://mainnet.base.org",
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const TIER_AMOUNTS = { 1: 1_000_000, 2: 5_000_000, 3: 50_000_000, 4: 500_000_000 };
|
|
89
|
+
const TIER_NAMES = { 1: "Spark", 2: "Circuit", 3: "Core", 4: "Architect" };
|
|
90
|
+
|
|
91
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
function bankrHeaders() {
|
|
94
|
+
return { "Content-Type": "application/json", "X-API-Key": BANKR_API_KEY };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function bankrGet(path) {
|
|
98
|
+
const r = await fetch(`${BANKR_BASE}${path}`, { headers: bankrHeaders() });
|
|
99
|
+
return r.json();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function bankrPost(path, body) {
|
|
103
|
+
const r = await fetch(`${BANKR_BASE}${path}`, {
|
|
104
|
+
method: "POST", headers: bankrHeaders(), body: JSON.stringify(body),
|
|
105
|
+
});
|
|
106
|
+
return r.json();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function coordGet(path, token) {
|
|
110
|
+
const headers = { "Content-Type": "application/json", "X-Litcoin-SDK": "mcp-1.0.0" };
|
|
111
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
112
|
+
const r = await fetch(`${COORDINATOR}${path}`, { headers });
|
|
113
|
+
return r.json();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function coordPost(path, body, token) {
|
|
117
|
+
const headers = { "Content-Type": "application/json", "X-Litcoin-SDK": "mcp-1.0.0" };
|
|
118
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
119
|
+
const r = await fetch(`${COORDINATOR}${path}`, {
|
|
120
|
+
method: "POST", headers, body: JSON.stringify(body),
|
|
121
|
+
});
|
|
122
|
+
return r.json();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function rpcCall(contract, calldata) {
|
|
126
|
+
for (const rpc of RPC_URLS) {
|
|
127
|
+
try {
|
|
128
|
+
const r = await fetch(rpc, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { "Content-Type": "application/json" },
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
jsonrpc: "2.0", id: 1, method: "eth_call",
|
|
133
|
+
params: [{ to: contract, data: calldata }, "latest"],
|
|
134
|
+
}),
|
|
135
|
+
signal: AbortSignal.timeout(8000),
|
|
136
|
+
});
|
|
137
|
+
const json = await r.json();
|
|
138
|
+
return json.result || "0x" + "0".repeat(64);
|
|
139
|
+
} catch { continue; }
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function padAddr(addr) {
|
|
145
|
+
return "000000000000000000000000" + addr.slice(2).toLowerCase();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function hex64(n) {
|
|
149
|
+
return BigInt(n).toString(16).padStart(64, "0");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function decodeUint(hex, slot = 0) {
|
|
153
|
+
if (!hex || hex === "0x") return 0n;
|
|
154
|
+
const d = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
155
|
+
const start = slot * 64;
|
|
156
|
+
if (start + 64 > d.length) return 0n;
|
|
157
|
+
return BigInt("0x" + d.slice(start, start + 64));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function toTokens(wei) {
|
|
161
|
+
return Number(wei) / 1e18;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function submitTx(to, calldata) {
|
|
165
|
+
return bankrPost("/agent/submit", {
|
|
166
|
+
transaction: { to, data: calldata, value: "0", chainId: 8453 },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function approveTx(token, spender, amountWei) {
|
|
171
|
+
const cd = SELECTORS.approve + padAddr(spender) + hex64(amountWei);
|
|
172
|
+
return submitTx(token, cd);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── Auth State ──────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
let _wallet = null;
|
|
178
|
+
let _authToken = null;
|
|
179
|
+
let _authExpiry = 0;
|
|
180
|
+
|
|
181
|
+
async function getWallet() {
|
|
182
|
+
if (_wallet) return _wallet;
|
|
183
|
+
const data = await bankrGet("/agent/me");
|
|
184
|
+
for (const key of ["wallets", "addresses"]) {
|
|
185
|
+
const items = data[key] || [];
|
|
186
|
+
if (Array.isArray(items)) {
|
|
187
|
+
for (const w of items) {
|
|
188
|
+
const addr = typeof w === "object" ? w.address : w;
|
|
189
|
+
if (typeof addr === "string" && addr.startsWith("0x")) {
|
|
190
|
+
_wallet = addr.toLowerCase();
|
|
191
|
+
return _wallet;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (const key of ["address", "evmAddress", "wallet"]) {
|
|
197
|
+
if (data[key] && typeof data[key] === "string" && data[key].startsWith("0x")) {
|
|
198
|
+
_wallet = data[key].toLowerCase();
|
|
199
|
+
return _wallet;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
throw new Error("No wallet found from Bankr");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function getAuth() {
|
|
206
|
+
if (_authToken && Date.now() / 1000 < _authExpiry - 60) return _authToken;
|
|
207
|
+
const wallet = await getWallet();
|
|
208
|
+
const nonceRes = await coordPost("/v1/auth/nonce", { miner: wallet });
|
|
209
|
+
const message = nonceRes.message;
|
|
210
|
+
const sigRes = await bankrPost("/agent/sign", {
|
|
211
|
+
signatureType: "personal_sign", message,
|
|
212
|
+
});
|
|
213
|
+
const verifyRes = await coordPost("/v1/auth/verify", {
|
|
214
|
+
miner: wallet, message, signature: sigRes.signature,
|
|
215
|
+
});
|
|
216
|
+
_authToken = verifyRes.token;
|
|
217
|
+
_authExpiry = Date.now() / 1000 + 3600;
|
|
218
|
+
return _authToken;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ─── MCP Server ──────────────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
const server = new McpServer({
|
|
224
|
+
name: "litcoin",
|
|
225
|
+
version: "1.0.0",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// ── Resources ────────────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
server.resource(
|
|
231
|
+
"protocol-docs",
|
|
232
|
+
"litcoin://docs",
|
|
233
|
+
async () => ({
|
|
234
|
+
contents: [{
|
|
235
|
+
uri: "litcoin://docs",
|
|
236
|
+
mimeType: "text/plain",
|
|
237
|
+
text: "Full LITCOIN protocol documentation available at https://litcoiin.xyz/docs.md — fetch this URL for complete SDK reference, contract addresses, and API endpoints.",
|
|
238
|
+
}],
|
|
239
|
+
})
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// ── Tools ────────────────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
// Balance
|
|
245
|
+
server.tool(
|
|
246
|
+
"litcoin_balance",
|
|
247
|
+
"Get LITCOIN and LITCREDIT token balances, staking tier, mining boost, and escrow balance for this wallet.",
|
|
248
|
+
{},
|
|
249
|
+
async () => {
|
|
250
|
+
const wallet = await getWallet();
|
|
251
|
+
const [litRaw, lcRaw, tierRaw, boostRaw, escrowRaw] = await Promise.all([
|
|
252
|
+
rpcCall(CONTRACTS.LITCOIN, SELECTORS.balanceOf + padAddr(wallet)),
|
|
253
|
+
rpcCall(CONTRACTS.LITCREDIT, SELECTORS.balanceOf + padAddr(wallet)),
|
|
254
|
+
rpcCall(CONTRACTS.STAKING, SELECTORS.getTier + padAddr(wallet)),
|
|
255
|
+
rpcCall(CONTRACTS.STAKING, SELECTORS.getMiningBoostBps + padAddr(wallet)),
|
|
256
|
+
rpcCall(CONTRACTS.COMPUTE_ESCROW, SELECTORS.available + padAddr(wallet)),
|
|
257
|
+
]);
|
|
258
|
+
const tier = Number(decodeUint(tierRaw));
|
|
259
|
+
return {
|
|
260
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
261
|
+
wallet,
|
|
262
|
+
litcoin: toTokens(decodeUint(litRaw)),
|
|
263
|
+
litcredit: toTokens(decodeUint(lcRaw)),
|
|
264
|
+
stakingTier: tier,
|
|
265
|
+
tierName: TIER_NAMES[tier] || "None",
|
|
266
|
+
miningBoost: `${(Number(decodeUint(boostRaw)) / 10000).toFixed(2)}x`,
|
|
267
|
+
escrowBalance: toTokens(decodeUint(escrowRaw)),
|
|
268
|
+
}, null, 2) }],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Network stats
|
|
274
|
+
server.tool(
|
|
275
|
+
"litcoin_network",
|
|
276
|
+
"Get LITCOIN network statistics: active miners, treasury balance, reward per solve, oracle prices.",
|
|
277
|
+
{},
|
|
278
|
+
async () => {
|
|
279
|
+
const [stats, cpiRaw, litRaw] = await Promise.all([
|
|
280
|
+
coordGet("/v1/claims/stats"),
|
|
281
|
+
rpcCall(CONTRACTS.ORACLE, SELECTORS.cpiPrice),
|
|
282
|
+
rpcCall(CONTRACTS.ORACLE, SELECTORS.litcoinPrice),
|
|
283
|
+
]);
|
|
284
|
+
return {
|
|
285
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
286
|
+
...stats,
|
|
287
|
+
oracleCpiPrice: toTokens(decodeUint(cpiRaw)),
|
|
288
|
+
oracleLitcoinPrice: toTokens(decodeUint(litRaw)),
|
|
289
|
+
}, null, 2) }],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Mine one round
|
|
295
|
+
server.tool(
|
|
296
|
+
"litcoin_mine",
|
|
297
|
+
"Mine one round of LITCOIN. Requests a challenge, solves it deterministically, and submits the answer. Returns the reward earned.",
|
|
298
|
+
{},
|
|
299
|
+
async () => {
|
|
300
|
+
const wallet = await getWallet();
|
|
301
|
+
const token = await getAuth();
|
|
302
|
+
const nonce = crypto.randomUUID();
|
|
303
|
+
|
|
304
|
+
// Get challenge
|
|
305
|
+
const challenge = await coordGet(`/v1/challenge?nonce=${nonce}`, token);
|
|
306
|
+
if (challenge.error) {
|
|
307
|
+
return { content: [{ type: "text", text: `Challenge error: ${challenge.error}` }] };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Solve deterministically (same logic as SDK solver)
|
|
311
|
+
const artifact = solveChallenge(challenge);
|
|
312
|
+
|
|
313
|
+
// Submit
|
|
314
|
+
const result = await coordPost("/v1/submit", {
|
|
315
|
+
challengeId: challenge.challengeId,
|
|
316
|
+
artifact,
|
|
317
|
+
nonce,
|
|
318
|
+
}, token);
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
322
|
+
pass: result.pass,
|
|
323
|
+
reward: result.reward,
|
|
324
|
+
boostMultiplier: result.boostMultiplier,
|
|
325
|
+
challengeId: challenge.challengeId,
|
|
326
|
+
}, null, 2) }],
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
// Claim rewards
|
|
332
|
+
server.tool(
|
|
333
|
+
"litcoin_claim",
|
|
334
|
+
"Claim accumulated mining rewards on-chain. Transfers earned LITCOIN to your wallet.",
|
|
335
|
+
{},
|
|
336
|
+
async () => {
|
|
337
|
+
const wallet = await getWallet();
|
|
338
|
+
const result = await coordPost("/v1/claims/bankr", { wallet });
|
|
339
|
+
return {
|
|
340
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Check claimable
|
|
346
|
+
server.tool(
|
|
347
|
+
"litcoin_claimable",
|
|
348
|
+
"Check how much LITCOIN is available to claim.",
|
|
349
|
+
{},
|
|
350
|
+
async () => {
|
|
351
|
+
const wallet = await getWallet();
|
|
352
|
+
const status = await coordGet(`/v1/claims/status?wallet=${wallet}`);
|
|
353
|
+
return {
|
|
354
|
+
content: [{ type: "text", text: JSON.stringify(status, null, 2) }],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Stake
|
|
360
|
+
server.tool(
|
|
361
|
+
"litcoin_stake",
|
|
362
|
+
"Stake LITCOIN into a tier. Tier 1=Spark (1M), 2=Circuit (5M), 3=Core (50M), 4=Architect (500M). Auto-approves tokens.",
|
|
363
|
+
{ tier: z.number().min(1).max(4).describe("Staking tier (1-4)") },
|
|
364
|
+
async ({ tier }) => {
|
|
365
|
+
const amountWei = BigInt(TIER_AMOUNTS[tier]) * 10n ** 18n;
|
|
366
|
+
await approveTx(CONTRACTS.LITCOIN, CONTRACTS.STAKING, amountWei);
|
|
367
|
+
const result = await submitTx(CONTRACTS.STAKING, SELECTORS.stake + hex64(tier));
|
|
368
|
+
return {
|
|
369
|
+
content: [{ type: "text", text: `Staked into ${TIER_NAMES[tier]} (tier ${tier}): ${JSON.stringify(result)}` }],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
// Unstake
|
|
375
|
+
server.tool(
|
|
376
|
+
"litcoin_unstake",
|
|
377
|
+
"Unstake LITCOIN. Only works after the lock period expires.",
|
|
378
|
+
{},
|
|
379
|
+
async () => {
|
|
380
|
+
const result = await submitTx(CONTRACTS.STAKING, SELECTORS.unstake);
|
|
381
|
+
return { content: [{ type: "text", text: `Unstaked: ${JSON.stringify(result)}` }] };
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// Open vault
|
|
386
|
+
server.tool(
|
|
387
|
+
"litcoin_open_vault",
|
|
388
|
+
"Open a vault with LITCOIN collateral to mint LITCREDIT. Specify collateral in whole tokens.",
|
|
389
|
+
{ collateral: z.number().positive().describe("LITCOIN collateral amount in whole tokens") },
|
|
390
|
+
async ({ collateral }) => {
|
|
391
|
+
const wei = BigInt(Math.floor(collateral)) * 10n ** 18n;
|
|
392
|
+
await approveTx(CONTRACTS.LITCOIN, CONTRACTS.VAULT_MANAGER, wei);
|
|
393
|
+
const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.openVault + hex64(wei));
|
|
394
|
+
return { content: [{ type: "text", text: `Vault opened with ${collateral} LITCOIN: ${JSON.stringify(result)}` }] };
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// Mint LITCREDIT
|
|
399
|
+
server.tool(
|
|
400
|
+
"litcoin_mint",
|
|
401
|
+
"Mint LITCREDIT against an open vault. 1 LITCREDIT = 1,000 output tokens of AI inference.",
|
|
402
|
+
{
|
|
403
|
+
vault_id: z.number().describe("Vault ID"),
|
|
404
|
+
amount: z.number().positive().describe("LITCREDIT to mint in whole tokens"),
|
|
405
|
+
},
|
|
406
|
+
async ({ vault_id, amount }) => {
|
|
407
|
+
const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
|
|
408
|
+
const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.mintLitCredit + hex64(vault_id) + hex64(wei));
|
|
409
|
+
return { content: [{ type: "text", text: `Minted ${amount} LITCREDIT from vault #${vault_id}: ${JSON.stringify(result)}` }] };
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
// Repay debt
|
|
414
|
+
server.tool(
|
|
415
|
+
"litcoin_repay",
|
|
416
|
+
"Repay LITCREDIT debt on a vault.",
|
|
417
|
+
{
|
|
418
|
+
vault_id: z.number().describe("Vault ID"),
|
|
419
|
+
amount: z.number().positive().describe("LITCREDIT to repay"),
|
|
420
|
+
},
|
|
421
|
+
async ({ vault_id, amount }) => {
|
|
422
|
+
const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
|
|
423
|
+
await approveTx(CONTRACTS.LITCREDIT, CONTRACTS.VAULT_MANAGER, wei);
|
|
424
|
+
const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.repayDebt + hex64(vault_id) + hex64(wei));
|
|
425
|
+
return { content: [{ type: "text", text: `Repaid ${amount} LITCREDIT on vault #${vault_id}: ${JSON.stringify(result)}` }] };
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// Add collateral
|
|
430
|
+
server.tool(
|
|
431
|
+
"litcoin_add_collateral",
|
|
432
|
+
"Add more LITCOIN collateral to an existing vault.",
|
|
433
|
+
{
|
|
434
|
+
vault_id: z.number().describe("Vault ID"),
|
|
435
|
+
amount: z.number().positive().describe("LITCOIN to add"),
|
|
436
|
+
},
|
|
437
|
+
async ({ vault_id, amount }) => {
|
|
438
|
+
const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
|
|
439
|
+
await approveTx(CONTRACTS.LITCOIN, CONTRACTS.VAULT_MANAGER, wei);
|
|
440
|
+
const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.addCollateral + hex64(vault_id) + hex64(wei));
|
|
441
|
+
return { content: [{ type: "text", text: `Added ${amount} LITCOIN to vault #${vault_id}: ${JSON.stringify(result)}` }] };
|
|
442
|
+
}
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// Close vault
|
|
446
|
+
server.tool(
|
|
447
|
+
"litcoin_close_vault",
|
|
448
|
+
"Close a vault. Must repay all debt first.",
|
|
449
|
+
{ vault_id: z.number().describe("Vault ID") },
|
|
450
|
+
async ({ vault_id }) => {
|
|
451
|
+
const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.closeVault + hex64(vault_id));
|
|
452
|
+
return { content: [{ type: "text", text: `Vault #${vault_id} closed: ${JSON.stringify(result)}` }] };
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// Vault info
|
|
457
|
+
server.tool(
|
|
458
|
+
"litcoin_vaults",
|
|
459
|
+
"List all vaults for this wallet with collateral, debt, health ratio, and liquidation status.",
|
|
460
|
+
{},
|
|
461
|
+
async () => {
|
|
462
|
+
const wallet = await getWallet();
|
|
463
|
+
const idsRaw = await rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getOwnerVaultIds + padAddr(wallet));
|
|
464
|
+
if (!idsRaw || idsRaw === "0x" || idsRaw.length < 130) {
|
|
465
|
+
return { content: [{ type: "text", text: "No vaults found." }] };
|
|
466
|
+
}
|
|
467
|
+
const data = idsRaw.slice(2);
|
|
468
|
+
const count = Number(BigInt("0x" + data.slice(64, 128)));
|
|
469
|
+
const vaults = [];
|
|
470
|
+
for (let i = 0; i < count; i++) {
|
|
471
|
+
const vid = Number(BigInt("0x" + data.slice(128 + i * 64, 192 + i * 64)));
|
|
472
|
+
const [healthRaw, mintRaw, liqRaw] = await Promise.all([
|
|
473
|
+
rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getVaultCollateralRatio + hex64(vid)),
|
|
474
|
+
rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getMaxMintable + hex64(vid)),
|
|
475
|
+
rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.isLiquidatable + hex64(vid)),
|
|
476
|
+
]);
|
|
477
|
+
vaults.push({
|
|
478
|
+
vaultId: vid,
|
|
479
|
+
healthRatioBps: Number(decodeUint(healthRaw)),
|
|
480
|
+
maxMintable: toTokens(decodeUint(mintRaw)),
|
|
481
|
+
liquidatable: decodeUint(liqRaw) !== 0n,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return { content: [{ type: "text", text: JSON.stringify(vaults, null, 2) }] };
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
// Deposit to escrow
|
|
489
|
+
server.tool(
|
|
490
|
+
"litcoin_deposit_escrow",
|
|
491
|
+
"Deposit LITCREDIT into the compute escrow for AI inference spending.",
|
|
492
|
+
{ amount: z.number().positive().describe("LITCREDIT to deposit") },
|
|
493
|
+
async ({ amount }) => {
|
|
494
|
+
const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
|
|
495
|
+
await approveTx(CONTRACTS.LITCREDIT, CONTRACTS.COMPUTE_ESCROW, wei);
|
|
496
|
+
const result = await submitTx(CONTRACTS.COMPUTE_ESCROW, SELECTORS.deposit + hex64(wei));
|
|
497
|
+
return { content: [{ type: "text", text: `Deposited ${amount} LITCREDIT to escrow: ${JSON.stringify(result)}` }] };
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
// Compute
|
|
502
|
+
server.tool(
|
|
503
|
+
"litcoin_compute",
|
|
504
|
+
"Send a prompt to the LITCOIN compute marketplace for AI inference. Uses LITCREDIT from escrow. Returns the AI response.",
|
|
505
|
+
{
|
|
506
|
+
prompt: z.string().describe("The prompt to send"),
|
|
507
|
+
model: z.string().optional().describe("Model name (default: llama-3.3-70b)"),
|
|
508
|
+
},
|
|
509
|
+
async ({ prompt, model }) => {
|
|
510
|
+
const result = await coordPost("/v1/compute/request", {
|
|
511
|
+
prompt,
|
|
512
|
+
model: model || "llama-3.3-70b",
|
|
513
|
+
maxTokens: 1024,
|
|
514
|
+
});
|
|
515
|
+
return {
|
|
516
|
+
content: [{ type: "text", text: result.response || JSON.stringify(result, null, 2) }],
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
// Create guild
|
|
522
|
+
server.tool(
|
|
523
|
+
"litcoin_create_guild",
|
|
524
|
+
"Create a new mining guild. You become the leader.",
|
|
525
|
+
{ name: z.string().describe("Guild name") },
|
|
526
|
+
async ({ name }) => {
|
|
527
|
+
const nameBytes = new TextEncoder().encode(name);
|
|
528
|
+
const nameHex = Array.from(nameBytes).map(b => b.toString(16).padStart(2, "0")).join("");
|
|
529
|
+
const padLen = (32 - nameBytes.length % 32) % 32;
|
|
530
|
+
const calldata = SELECTORS.createGuild +
|
|
531
|
+
hex64(32) +
|
|
532
|
+
hex64(nameBytes.length) +
|
|
533
|
+
nameHex + "00".repeat(padLen);
|
|
534
|
+
const result = await submitTx(CONTRACTS.GUILD, calldata);
|
|
535
|
+
return { content: [{ type: "text", text: `Guild "${name}" created: ${JSON.stringify(result)}` }] };
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
// Join guild
|
|
540
|
+
server.tool(
|
|
541
|
+
"litcoin_join_guild",
|
|
542
|
+
"Join a mining guild with a LITCOIN deposit.",
|
|
543
|
+
{
|
|
544
|
+
guild_id: z.number().describe("Guild ID"),
|
|
545
|
+
amount: z.number().positive().describe("LITCOIN to deposit"),
|
|
546
|
+
},
|
|
547
|
+
async ({ guild_id, amount }) => {
|
|
548
|
+
const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
|
|
549
|
+
await approveTx(CONTRACTS.LITCOIN, CONTRACTS.GUILD, wei);
|
|
550
|
+
const result = await submitTx(CONTRACTS.GUILD, SELECTORS.joinGuild + hex64(guild_id) + hex64(wei));
|
|
551
|
+
return { content: [{ type: "text", text: `Joined guild #${guild_id} with ${amount} LITCOIN: ${JSON.stringify(result)}` }] };
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
// Leave guild
|
|
556
|
+
server.tool(
|
|
557
|
+
"litcoin_leave_guild",
|
|
558
|
+
"Leave your current mining guild. Returns your deposited tokens.",
|
|
559
|
+
{},
|
|
560
|
+
async () => {
|
|
561
|
+
const result = await submitTx(CONTRACTS.GUILD, SELECTORS.leaveGuild);
|
|
562
|
+
return { content: [{ type: "text", text: `Left guild: ${JSON.stringify(result)}` }] };
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Faucet
|
|
567
|
+
server.tool(
|
|
568
|
+
"litcoin_faucet",
|
|
569
|
+
"Claim 5M LITCOIN from the bootstrap faucet. One-time per wallet. Solves a trial challenge to prove AI capability.",
|
|
570
|
+
{},
|
|
571
|
+
async () => {
|
|
572
|
+
const wallet = await getWallet();
|
|
573
|
+
// Get faucet challenge
|
|
574
|
+
const challenge = await coordPost("/v1/faucet/challenge", {});
|
|
575
|
+
if (challenge.error) {
|
|
576
|
+
return { content: [{ type: "text", text: `Faucet error: ${challenge.error}` }] };
|
|
577
|
+
}
|
|
578
|
+
// Solve it
|
|
579
|
+
const artifact = solveChallenge(challenge);
|
|
580
|
+
// Submit
|
|
581
|
+
const result = await coordPost("/v1/faucet/submit", {
|
|
582
|
+
challengeId: challenge.challengeId || challenge.benchmarkId,
|
|
583
|
+
artifact,
|
|
584
|
+
wallet,
|
|
585
|
+
});
|
|
586
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
// ─── Challenge Solver (deterministic — same as SDK) ──────────────────────────
|
|
591
|
+
|
|
592
|
+
function solveChallenge(challenge) {
|
|
593
|
+
const { doc, questions, constraints, entities } = challenge;
|
|
594
|
+
if (!doc || !questions) return "";
|
|
595
|
+
|
|
596
|
+
const answers = [];
|
|
597
|
+
|
|
598
|
+
for (const q of questions) {
|
|
599
|
+
// Try to find answer in document
|
|
600
|
+
let answer = "";
|
|
601
|
+
|
|
602
|
+
// Check entities for matching data
|
|
603
|
+
if (entities) {
|
|
604
|
+
for (const ent of entities) {
|
|
605
|
+
if (q.toLowerCase().includes(ent.name?.toLowerCase() || "")) {
|
|
606
|
+
// Return relevant entity data
|
|
607
|
+
if (ent.revenue) answer = ent.revenue;
|
|
608
|
+
else if (ent.founded) answer = String(ent.founded);
|
|
609
|
+
else if (ent.role) answer = ent.role;
|
|
610
|
+
else if (ent.value) answer = String(ent.value);
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (!answer) {
|
|
617
|
+
// Extract from constraints
|
|
618
|
+
for (const c of (constraints || [])) {
|
|
619
|
+
if (typeof c === "string" && c.includes("=")) {
|
|
620
|
+
const [key, val] = c.split("=").map(s => s.trim());
|
|
621
|
+
if (q.toLowerCase().includes(key.toLowerCase())) {
|
|
622
|
+
answer = val;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (!answer) answer = "N/A";
|
|
630
|
+
answers.push(answer);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Compute ASCII checksum
|
|
634
|
+
const joined = answers.join("|");
|
|
635
|
+
let checksum = 0;
|
|
636
|
+
for (let i = 0; i < joined.length; i++) {
|
|
637
|
+
checksum = (checksum + joined.charCodeAt(i)) % 256;
|
|
638
|
+
}
|
|
639
|
+
answers.push(String(checksum));
|
|
640
|
+
|
|
641
|
+
return answers.join("|");
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ─── Start ───────────────────────────────────────────────────────────────────
|
|
645
|
+
|
|
646
|
+
async function main() {
|
|
647
|
+
if (!BANKR_API_KEY) {
|
|
648
|
+
console.error("BANKR_API_KEY environment variable required. Get one at https://bankr.bot/api");
|
|
649
|
+
process.exit(1);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const transport = new StdioServerTransport();
|
|
653
|
+
await server.connect(transport);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
main().catch(console.error);
|