@hardclaw/mcp-server 0.1.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 +132 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +275 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.d.ts +48 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +106 -0
- package/dist/sdk.js.map +1 -0
- package/package.json +41 -0
- package/src/index.ts +312 -0
- package/src/sdk.ts +164 -0
- package/tsconfig.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# HardClaw MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server and TypeScript SDK for the HardClaw Proof-of-Verification protocol.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @hardclaw/mcp-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @hardclaw/mcp-server
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## MCP Configuration
|
|
18
|
+
|
|
19
|
+
Add to your MCP client settings:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"hardclaw": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "@hardclaw/mcp-server"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Available Tools
|
|
33
|
+
|
|
34
|
+
### Wallet Management
|
|
35
|
+
- **create_wallet** - Generate a new wallet with BIP39 mnemonic
|
|
36
|
+
- **load_wallet** - Load wallet from mnemonic phrase
|
|
37
|
+
- **get_address** - Get current wallet address
|
|
38
|
+
|
|
39
|
+
### Job Lifecycle
|
|
40
|
+
- **submit_job** - Submit a job with bounty
|
|
41
|
+
- `description`: Job description
|
|
42
|
+
- `input`: Input data (hex or plain text)
|
|
43
|
+
- `bounty`: Bounty in HCLAW tokens
|
|
44
|
+
- `jobType`: "deterministic" or "subjective"
|
|
45
|
+
- `expectedHash`: Expected output hash (optional, for deterministic)
|
|
46
|
+
- `timeout`: Timeout in seconds (default: 3600)
|
|
47
|
+
|
|
48
|
+
- **submit_solution** - Submit solution for a job
|
|
49
|
+
- `jobId`: Job ID (hex hash)
|
|
50
|
+
- `output`: Solution output (hex or plain text)
|
|
51
|
+
|
|
52
|
+
- **verify_solution** - Verify a solution (verifier operation)
|
|
53
|
+
- `jobId`: Job ID
|
|
54
|
+
- `solutionId`: Solution ID
|
|
55
|
+
|
|
56
|
+
### Network
|
|
57
|
+
- **get_balance** - Check wallet balance
|
|
58
|
+
- **list_jobs** - List available jobs
|
|
59
|
+
|
|
60
|
+
## Usage Example
|
|
61
|
+
|
|
62
|
+
### For Agents (Solvers)
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// 1. Create or load wallet
|
|
66
|
+
await mcp.call("create_wallet");
|
|
67
|
+
|
|
68
|
+
// 2. Wait for jobs from requesters
|
|
69
|
+
await mcp.call("list_jobs", { limit: 5 });
|
|
70
|
+
|
|
71
|
+
// 3. Execute work off-chain
|
|
72
|
+
|
|
73
|
+
// 4. Submit solution
|
|
74
|
+
await mcp.call("submit_solution", {
|
|
75
|
+
jobId: "abc123...",
|
|
76
|
+
output: "computation result here"
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// 5. Earn 95% of bounty when verified
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### For Requesters
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// 1. Load wallet
|
|
86
|
+
await mcp.call("load_wallet", {
|
|
87
|
+
mnemonic: "your twelve or twenty four words..."
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 2. Submit job
|
|
91
|
+
await mcp.call("submit_job", {
|
|
92
|
+
description: "Analyze sentiment of text",
|
|
93
|
+
input: "The product exceeded expectations",
|
|
94
|
+
bounty: 10,
|
|
95
|
+
jobType: "subjective"
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### For Verifiers
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// 1. Load wallet with stake
|
|
103
|
+
await mcp.call("load_wallet", { mnemonic: "..." });
|
|
104
|
+
|
|
105
|
+
// 2. Verify solutions
|
|
106
|
+
await mcp.call("verify_solution", {
|
|
107
|
+
jobId: "abc123...",
|
|
108
|
+
solutionId: "def456..."
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// 3. Earn 4% of bounty
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Network Integration
|
|
115
|
+
|
|
116
|
+
The MCP server operates in local/simulation mode by default. To connect to the live network:
|
|
117
|
+
|
|
118
|
+
1. Install and run `hardclaw-node`
|
|
119
|
+
2. The MCP server will auto-detect and connect
|
|
120
|
+
3. All operations will be broadcast to the network
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm install
|
|
126
|
+
npm run build
|
|
127
|
+
npm start
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { HardClawSDK } from "./sdk.js";
|
|
6
|
+
const sdk = new HardClawSDK();
|
|
7
|
+
const server = new Server({
|
|
8
|
+
name: "hardclaw-mcp",
|
|
9
|
+
version: "0.1.0",
|
|
10
|
+
}, {
|
|
11
|
+
capabilities: {
|
|
12
|
+
tools: {},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
const tools = [
|
|
16
|
+
{
|
|
17
|
+
name: "create_wallet",
|
|
18
|
+
description: "Create a new HardClaw wallet with BIP39 mnemonic",
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {},
|
|
22
|
+
required: [],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "load_wallet",
|
|
27
|
+
description: "Load wallet from mnemonic phrase",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
mnemonic: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "BIP39 mnemonic phrase (12 or 24 words)",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["mnemonic"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "get_address",
|
|
41
|
+
description: "Get the current wallet address",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {},
|
|
45
|
+
required: [],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "submit_job",
|
|
50
|
+
description: "Submit a job to the HardClaw network with a bounty",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
description: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Description of the job",
|
|
57
|
+
},
|
|
58
|
+
input: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Input data (hex-encoded or plain text)",
|
|
61
|
+
},
|
|
62
|
+
bounty: {
|
|
63
|
+
type: "number",
|
|
64
|
+
description: "Bounty amount in HCLAW tokens",
|
|
65
|
+
},
|
|
66
|
+
jobType: {
|
|
67
|
+
type: "string",
|
|
68
|
+
enum: ["deterministic", "subjective"],
|
|
69
|
+
description: "Type of job verification",
|
|
70
|
+
},
|
|
71
|
+
expectedHash: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Expected output hash for deterministic jobs (optional)",
|
|
74
|
+
},
|
|
75
|
+
timeout: {
|
|
76
|
+
type: "number",
|
|
77
|
+
description: "Job timeout in seconds (default: 3600)",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["description", "input", "bounty", "jobType"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "submit_solution",
|
|
85
|
+
description: "Submit a solution for a job",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
jobId: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Job ID (hex hash)",
|
|
92
|
+
},
|
|
93
|
+
output: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Solution output data (hex-encoded or plain text)",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ["jobId", "output"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "verify_solution",
|
|
103
|
+
description: "Verify a solution against a job (verifier operation)",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
jobId: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "Job ID (hex hash)",
|
|
110
|
+
},
|
|
111
|
+
solutionId: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Solution ID (hex hash)",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
required: ["jobId", "solutionId"],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "get_balance",
|
|
121
|
+
description: "Get wallet balance (requires network connection)",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {},
|
|
125
|
+
required: [],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "list_jobs",
|
|
130
|
+
description: "List available jobs from the network",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
limit: {
|
|
135
|
+
type: "number",
|
|
136
|
+
description: "Maximum number of jobs to return (default: 10)",
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
required: [],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
144
|
+
tools,
|
|
145
|
+
}));
|
|
146
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
147
|
+
const { name, arguments: args } = request.params;
|
|
148
|
+
try {
|
|
149
|
+
switch (name) {
|
|
150
|
+
case "create_wallet": {
|
|
151
|
+
const result = sdk.createWallet();
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: `Created new wallet\nAddress: ${result.address}\nMnemonic: ${result.mnemonic}\n\n⚠️ Save this mnemonic securely!`,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case "load_wallet": {
|
|
162
|
+
const { mnemonic } = args;
|
|
163
|
+
const address = sdk.loadWallet(mnemonic);
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `Loaded wallet\nAddress: ${address}`,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
case "get_address": {
|
|
174
|
+
const address = sdk.getAddress();
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: address,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case "submit_job": {
|
|
185
|
+
const { description, input, bounty, jobType, expectedHash, timeout } = args;
|
|
186
|
+
const job = sdk.createJob({
|
|
187
|
+
description,
|
|
188
|
+
input,
|
|
189
|
+
bounty,
|
|
190
|
+
jobType,
|
|
191
|
+
expectedHash,
|
|
192
|
+
timeout: timeout || 3600,
|
|
193
|
+
});
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: `Job created:\n ID: ${job.id}\n Type: ${job.jobType}\n Bounty: ${bounty} HCLAW\n Burn: 1 HCLAW\n Timeout: ${job.timeout}s\n\n📡 Broadcast this to the network using hardclaw-node`,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
case "submit_solution": {
|
|
204
|
+
const { jobId, output } = args;
|
|
205
|
+
const solution = sdk.createSolution(jobId, output);
|
|
206
|
+
return {
|
|
207
|
+
content: [
|
|
208
|
+
{
|
|
209
|
+
type: "text",
|
|
210
|
+
text: `Solution created:\n Job ID: ${jobId}\n Solution ID: ${solution.id}\n Solver: ${solution.solver}\n\n📡 Broadcast this to the network using hardclaw-node`,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
case "verify_solution": {
|
|
216
|
+
const { jobId, solutionId } = args;
|
|
217
|
+
const result = sdk.verifySolution(jobId, solutionId);
|
|
218
|
+
return {
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: "text",
|
|
222
|
+
text: `Verification result:\n Valid: ${result.valid}\n Reason: ${result.reason || "N/A"}\n\n${result.valid ? "✅ Solution passes verification" : "❌ Solution failed verification"}`,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
case "get_balance": {
|
|
228
|
+
const address = sdk.getAddress();
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: `Balance for ${address}: 0.0 HCLAW\n\n⚠️ Balance lookup requires connection to hardclaw-node`,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
case "list_jobs": {
|
|
239
|
+
const { limit = 10 } = args;
|
|
240
|
+
return {
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: "text",
|
|
244
|
+
text: `Fetching ${limit} jobs from network...\n\n⚠️ Job listing requires connection to hardclaw-node`,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
default:
|
|
250
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
255
|
+
return {
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: "text",
|
|
259
|
+
text: `Error: ${message}`,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
isError: true,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
async function main() {
|
|
267
|
+
const transport = new StdioServerTransport();
|
|
268
|
+
await server.connect(transport);
|
|
269
|
+
console.error("HardClaw MCP server running on stdio");
|
|
270
|
+
}
|
|
271
|
+
main().catch((error) => {
|
|
272
|
+
console.error("Fatal error:", error);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|
|
275
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface Job {
|
|
2
|
+
id: string;
|
|
3
|
+
jobType: "deterministic" | "subjective";
|
|
4
|
+
requester: string;
|
|
5
|
+
description: string;
|
|
6
|
+
input: string;
|
|
7
|
+
bounty: number;
|
|
8
|
+
burnFee: number;
|
|
9
|
+
timeout: number;
|
|
10
|
+
expectedHash?: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
export interface Solution {
|
|
14
|
+
id: string;
|
|
15
|
+
jobId: string;
|
|
16
|
+
solver: string;
|
|
17
|
+
output: string;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
signature: string;
|
|
20
|
+
}
|
|
21
|
+
export interface VerificationResult {
|
|
22
|
+
valid: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class HardClawSDK {
|
|
26
|
+
private keypair;
|
|
27
|
+
private mnemonic;
|
|
28
|
+
createWallet(): {
|
|
29
|
+
address: string;
|
|
30
|
+
mnemonic: string;
|
|
31
|
+
};
|
|
32
|
+
loadWallet(mnemonic: string): string;
|
|
33
|
+
getAddress(): string;
|
|
34
|
+
createJob(params: {
|
|
35
|
+
description: string;
|
|
36
|
+
input: string;
|
|
37
|
+
bounty: number;
|
|
38
|
+
jobType: "deterministic" | "subjective";
|
|
39
|
+
expectedHash?: string;
|
|
40
|
+
timeout: number;
|
|
41
|
+
}): Job;
|
|
42
|
+
createSolution(jobId: string, output: string): Solution;
|
|
43
|
+
verifySolution(jobId: string, solutionId: string): VerificationResult;
|
|
44
|
+
private publicKeyToAddress;
|
|
45
|
+
private hash;
|
|
46
|
+
private encodeInput;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,eAAe,GAAG,YAAY,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,QAAQ,CAAuB;IAEvC,YAAY,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAWrD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAapC,UAAU,IAAI,MAAM;IAOpB,SAAS,CAAC,MAAM,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,eAAe,GAAG,YAAY,CAAC;QACxC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,GAAG;IAgCP,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ;IA2BvD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,kBAAkB;IAarE,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,WAAW;CAQpB"}
|
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as nacl from "tweetnacl";
|
|
2
|
+
import * as bip39 from "bip39";
|
|
3
|
+
import { derivePath } from "ed25519-hd-key";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
export class HardClawSDK {
|
|
6
|
+
keypair = null;
|
|
7
|
+
mnemonic = null;
|
|
8
|
+
createWallet() {
|
|
9
|
+
this.mnemonic = bip39.generateMnemonic(256); // 24 words
|
|
10
|
+
const seed = bip39.mnemonicToSeedSync(this.mnemonic);
|
|
11
|
+
const derivedSeed = derivePath("m/44'/501'/0'/0'", seed.toString("hex")).key;
|
|
12
|
+
this.keypair = nacl.sign.keyPair.fromSeed(derivedSeed);
|
|
13
|
+
const address = this.publicKeyToAddress(this.keypair.publicKey);
|
|
14
|
+
return { address, mnemonic: this.mnemonic };
|
|
15
|
+
}
|
|
16
|
+
loadWallet(mnemonic) {
|
|
17
|
+
if (!bip39.validateMnemonic(mnemonic)) {
|
|
18
|
+
throw new Error("Invalid mnemonic phrase");
|
|
19
|
+
}
|
|
20
|
+
this.mnemonic = mnemonic;
|
|
21
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
22
|
+
const derivedSeed = derivePath("m/44'/501'/0'/0'", seed.toString("hex")).key;
|
|
23
|
+
this.keypair = nacl.sign.keyPair.fromSeed(derivedSeed);
|
|
24
|
+
return this.publicKeyToAddress(this.keypair.publicKey);
|
|
25
|
+
}
|
|
26
|
+
getAddress() {
|
|
27
|
+
if (!this.keypair) {
|
|
28
|
+
throw new Error("No wallet loaded. Use createWallet() or loadWallet() first.");
|
|
29
|
+
}
|
|
30
|
+
return this.publicKeyToAddress(this.keypair.publicKey);
|
|
31
|
+
}
|
|
32
|
+
createJob(params) {
|
|
33
|
+
if (!this.keypair) {
|
|
34
|
+
throw new Error("No wallet loaded");
|
|
35
|
+
}
|
|
36
|
+
const inputData = this.encodeInput(params.input);
|
|
37
|
+
const expectedHash = params.expectedHash || this.hash(Buffer.from(inputData, "hex"));
|
|
38
|
+
const job = {
|
|
39
|
+
id: this.hash(Buffer.concat([
|
|
40
|
+
this.keypair.publicKey,
|
|
41
|
+
Buffer.from(inputData, "hex"),
|
|
42
|
+
Buffer.from(params.description),
|
|
43
|
+
Buffer.from(Date.now().toString()),
|
|
44
|
+
])),
|
|
45
|
+
jobType: params.jobType,
|
|
46
|
+
requester: this.publicKeyToAddress(this.keypair.publicKey),
|
|
47
|
+
description: params.description,
|
|
48
|
+
input: inputData,
|
|
49
|
+
bounty: params.bounty,
|
|
50
|
+
burnFee: 1,
|
|
51
|
+
timeout: params.timeout,
|
|
52
|
+
expectedHash: params.jobType === "deterministic" ? expectedHash : undefined,
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
};
|
|
55
|
+
return job;
|
|
56
|
+
}
|
|
57
|
+
createSolution(jobId, output) {
|
|
58
|
+
if (!this.keypair) {
|
|
59
|
+
throw new Error("No wallet loaded");
|
|
60
|
+
}
|
|
61
|
+
const outputData = this.encodeInput(output);
|
|
62
|
+
const solutionData = Buffer.concat([
|
|
63
|
+
Buffer.from(jobId, "hex"),
|
|
64
|
+
this.keypair.publicKey,
|
|
65
|
+
Buffer.from(outputData, "hex"),
|
|
66
|
+
Buffer.from(Date.now().toString()),
|
|
67
|
+
]);
|
|
68
|
+
const signature = nacl.sign.detached(solutionData, this.keypair.secretKey);
|
|
69
|
+
const solution = {
|
|
70
|
+
id: this.hash(solutionData),
|
|
71
|
+
jobId,
|
|
72
|
+
solver: this.publicKeyToAddress(this.keypair.publicKey),
|
|
73
|
+
output: outputData,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
76
|
+
};
|
|
77
|
+
return solution;
|
|
78
|
+
}
|
|
79
|
+
verifySolution(jobId, solutionId) {
|
|
80
|
+
// Simulated verification - in production this would:
|
|
81
|
+
// 1. Fetch the job from network
|
|
82
|
+
// 2. Fetch the solution from network
|
|
83
|
+
// 3. Verify signature
|
|
84
|
+
// 4. Check hash match (deterministic) or run Schelling voting (subjective)
|
|
85
|
+
return {
|
|
86
|
+
valid: true,
|
|
87
|
+
reason: "Verification simulated - connect to hardclaw-node for real verification",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
publicKeyToAddress(publicKey) {
|
|
91
|
+
const hash = this.hash(Buffer.from(publicKey));
|
|
92
|
+
return `hc1${hash.slice(0, 40)}`;
|
|
93
|
+
}
|
|
94
|
+
hash(data) {
|
|
95
|
+
return createHash("blake2s256").update(data).digest("hex");
|
|
96
|
+
}
|
|
97
|
+
encodeInput(input) {
|
|
98
|
+
// If already hex, return as-is
|
|
99
|
+
if (/^[0-9a-f]+$/i.test(input)) {
|
|
100
|
+
return input.toLowerCase();
|
|
101
|
+
}
|
|
102
|
+
// Otherwise encode as hex
|
|
103
|
+
return Buffer.from(input).toString("hex");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=sdk.js.map
|
package/dist/sdk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hardclaw/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server for HardClaw - Proof-of-Verification Protocol",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"hardclaw-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"hardclaw",
|
|
20
|
+
"blockchain",
|
|
21
|
+
"proof-of-verification",
|
|
22
|
+
"agents"
|
|
23
|
+
],
|
|
24
|
+
"author": "HardClaw Collective",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
28
|
+
"ed25519-hd-key": "^1.3.0",
|
|
29
|
+
"tweetnacl": "^1.0.3",
|
|
30
|
+
"bip39": "^3.1.0",
|
|
31
|
+
"blake3": "^2.1.7",
|
|
32
|
+
"zod": "^3.22.4"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.0",
|
|
36
|
+
"typescript": "^5.3.3"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
Tool,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { HardClawSDK } from "./sdk.js";
|
|
11
|
+
|
|
12
|
+
const sdk = new HardClawSDK();
|
|
13
|
+
|
|
14
|
+
const server = new Server(
|
|
15
|
+
{
|
|
16
|
+
name: "hardclaw-mcp",
|
|
17
|
+
version: "0.1.0",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
capabilities: {
|
|
21
|
+
tools: {},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const tools: Tool[] = [
|
|
27
|
+
{
|
|
28
|
+
name: "create_wallet",
|
|
29
|
+
description: "Create a new HardClaw wallet with BIP39 mnemonic",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {},
|
|
33
|
+
required: [],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "load_wallet",
|
|
38
|
+
description: "Load wallet from mnemonic phrase",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
mnemonic: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "BIP39 mnemonic phrase (12 or 24 words)",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: ["mnemonic"],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "get_address",
|
|
52
|
+
description: "Get the current wallet address",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {},
|
|
56
|
+
required: [],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "submit_job",
|
|
61
|
+
description: "Submit a job to the HardClaw network with a bounty",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
description: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Description of the job",
|
|
68
|
+
},
|
|
69
|
+
input: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Input data (hex-encoded or plain text)",
|
|
72
|
+
},
|
|
73
|
+
bounty: {
|
|
74
|
+
type: "number",
|
|
75
|
+
description: "Bounty amount in HCLAW tokens",
|
|
76
|
+
},
|
|
77
|
+
jobType: {
|
|
78
|
+
type: "string",
|
|
79
|
+
enum: ["deterministic", "subjective"],
|
|
80
|
+
description: "Type of job verification",
|
|
81
|
+
},
|
|
82
|
+
expectedHash: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Expected output hash for deterministic jobs (optional)",
|
|
85
|
+
},
|
|
86
|
+
timeout: {
|
|
87
|
+
type: "number",
|
|
88
|
+
description: "Job timeout in seconds (default: 3600)",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
required: ["description", "input", "bounty", "jobType"],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "submit_solution",
|
|
96
|
+
description: "Submit a solution for a job",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
jobId: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Job ID (hex hash)",
|
|
103
|
+
},
|
|
104
|
+
output: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "Solution output data (hex-encoded or plain text)",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ["jobId", "output"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "verify_solution",
|
|
114
|
+
description: "Verify a solution against a job (verifier operation)",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
jobId: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "Job ID (hex hash)",
|
|
121
|
+
},
|
|
122
|
+
solutionId: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "Solution ID (hex hash)",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
required: ["jobId", "solutionId"],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "get_balance",
|
|
132
|
+
description: "Get wallet balance (requires network connection)",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {},
|
|
136
|
+
required: [],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "list_jobs",
|
|
141
|
+
description: "List available jobs from the network",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
limit: {
|
|
146
|
+
type: "number",
|
|
147
|
+
description: "Maximum number of jobs to return (default: 10)",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
required: [],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
156
|
+
tools,
|
|
157
|
+
}));
|
|
158
|
+
|
|
159
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
160
|
+
const { name, arguments: args } = request.params;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
switch (name) {
|
|
164
|
+
case "create_wallet": {
|
|
165
|
+
const result = sdk.createWallet();
|
|
166
|
+
return {
|
|
167
|
+
content: [
|
|
168
|
+
{
|
|
169
|
+
type: "text",
|
|
170
|
+
text: `Created new wallet\nAddress: ${result.address}\nMnemonic: ${result.mnemonic}\n\n⚠️ Save this mnemonic securely!`,
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case "load_wallet": {
|
|
177
|
+
const { mnemonic } = args as { mnemonic: string };
|
|
178
|
+
const address = sdk.loadWallet(mnemonic);
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: `Loaded wallet\nAddress: ${address}`,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "get_address": {
|
|
190
|
+
const address = sdk.getAddress();
|
|
191
|
+
return {
|
|
192
|
+
content: [
|
|
193
|
+
{
|
|
194
|
+
type: "text",
|
|
195
|
+
text: address,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
case "submit_job": {
|
|
202
|
+
const { description, input, bounty, jobType, expectedHash, timeout } =
|
|
203
|
+
args as {
|
|
204
|
+
description: string;
|
|
205
|
+
input: string;
|
|
206
|
+
bounty: number;
|
|
207
|
+
jobType: "deterministic" | "subjective";
|
|
208
|
+
expectedHash?: string;
|
|
209
|
+
timeout?: number;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const job = sdk.createJob({
|
|
213
|
+
description,
|
|
214
|
+
input,
|
|
215
|
+
bounty,
|
|
216
|
+
jobType,
|
|
217
|
+
expectedHash,
|
|
218
|
+
timeout: timeout || 3600,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: `Job created:\n ID: ${job.id}\n Type: ${job.jobType}\n Bounty: ${bounty} HCLAW\n Burn: 1 HCLAW\n Timeout: ${job.timeout}s\n\n📡 Broadcast this to the network using hardclaw-node`,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
case "submit_solution": {
|
|
232
|
+
const { jobId, output } = args as { jobId: string; output: string };
|
|
233
|
+
const solution = sdk.createSolution(jobId, output);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: `Solution created:\n Job ID: ${jobId}\n Solution ID: ${solution.id}\n Solver: ${solution.solver}\n\n📡 Broadcast this to the network using hardclaw-node`,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
case "verify_solution": {
|
|
246
|
+
const { jobId, solutionId } = args as {
|
|
247
|
+
jobId: string;
|
|
248
|
+
solutionId: string;
|
|
249
|
+
};
|
|
250
|
+
const result = sdk.verifySolution(jobId, solutionId);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
content: [
|
|
254
|
+
{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: `Verification result:\n Valid: ${result.valid}\n Reason: ${result.reason || "N/A"}\n\n${result.valid ? "✅ Solution passes verification" : "❌ Solution failed verification"}`,
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case "get_balance": {
|
|
263
|
+
const address = sdk.getAddress();
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "text",
|
|
268
|
+
text: `Balance for ${address}: 0.0 HCLAW\n\n⚠️ Balance lookup requires connection to hardclaw-node`,
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
case "list_jobs": {
|
|
275
|
+
const { limit = 10 } = args as { limit?: number };
|
|
276
|
+
return {
|
|
277
|
+
content: [
|
|
278
|
+
{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: `Fetching ${limit} jobs from network...\n\n⚠️ Job listing requires connection to hardclaw-node`,
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
default:
|
|
287
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
291
|
+
return {
|
|
292
|
+
content: [
|
|
293
|
+
{
|
|
294
|
+
type: "text",
|
|
295
|
+
text: `Error: ${message}`,
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
isError: true,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
async function main() {
|
|
304
|
+
const transport = new StdioServerTransport();
|
|
305
|
+
await server.connect(transport);
|
|
306
|
+
console.error("HardClaw MCP server running on stdio");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
main().catch((error) => {
|
|
310
|
+
console.error("Fatal error:", error);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
});
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as nacl from "tweetnacl";
|
|
2
|
+
import * as bip39 from "bip39";
|
|
3
|
+
import { derivePath } from "ed25519-hd-key";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
|
|
6
|
+
export interface Job {
|
|
7
|
+
id: string;
|
|
8
|
+
jobType: "deterministic" | "subjective";
|
|
9
|
+
requester: string;
|
|
10
|
+
description: string;
|
|
11
|
+
input: string;
|
|
12
|
+
bounty: number;
|
|
13
|
+
burnFee: number;
|
|
14
|
+
timeout: number;
|
|
15
|
+
expectedHash?: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Solution {
|
|
20
|
+
id: string;
|
|
21
|
+
jobId: string;
|
|
22
|
+
solver: string;
|
|
23
|
+
output: string;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
signature: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface VerificationResult {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class HardClawSDK {
|
|
34
|
+
private keypair: nacl.SignKeyPair | null = null;
|
|
35
|
+
private mnemonic: string | null = null;
|
|
36
|
+
|
|
37
|
+
createWallet(): { address: string; mnemonic: string } {
|
|
38
|
+
this.mnemonic = bip39.generateMnemonic(256); // 24 words
|
|
39
|
+
const seed = bip39.mnemonicToSeedSync(this.mnemonic);
|
|
40
|
+
const derivedSeed = derivePath("m/44'/501'/0'/0'", seed.toString("hex")).key;
|
|
41
|
+
|
|
42
|
+
this.keypair = nacl.sign.keyPair.fromSeed(derivedSeed);
|
|
43
|
+
const address = this.publicKeyToAddress(this.keypair.publicKey);
|
|
44
|
+
|
|
45
|
+
return { address, mnemonic: this.mnemonic };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
loadWallet(mnemonic: string): string {
|
|
49
|
+
if (!bip39.validateMnemonic(mnemonic)) {
|
|
50
|
+
throw new Error("Invalid mnemonic phrase");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.mnemonic = mnemonic;
|
|
54
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
55
|
+
const derivedSeed = derivePath("m/44'/501'/0'/0'", seed.toString("hex")).key;
|
|
56
|
+
|
|
57
|
+
this.keypair = nacl.sign.keyPair.fromSeed(derivedSeed);
|
|
58
|
+
return this.publicKeyToAddress(this.keypair.publicKey);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getAddress(): string {
|
|
62
|
+
if (!this.keypair) {
|
|
63
|
+
throw new Error("No wallet loaded. Use createWallet() or loadWallet() first.");
|
|
64
|
+
}
|
|
65
|
+
return this.publicKeyToAddress(this.keypair.publicKey);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
createJob(params: {
|
|
69
|
+
description: string;
|
|
70
|
+
input: string;
|
|
71
|
+
bounty: number;
|
|
72
|
+
jobType: "deterministic" | "subjective";
|
|
73
|
+
expectedHash?: string;
|
|
74
|
+
timeout: number;
|
|
75
|
+
}): Job {
|
|
76
|
+
if (!this.keypair) {
|
|
77
|
+
throw new Error("No wallet loaded");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const inputData = this.encodeInput(params.input);
|
|
81
|
+
const expectedHash =
|
|
82
|
+
params.expectedHash || this.hash(Buffer.from(inputData, "hex"));
|
|
83
|
+
|
|
84
|
+
const job: Job = {
|
|
85
|
+
id: this.hash(
|
|
86
|
+
Buffer.concat([
|
|
87
|
+
this.keypair.publicKey,
|
|
88
|
+
Buffer.from(inputData, "hex"),
|
|
89
|
+
Buffer.from(params.description),
|
|
90
|
+
Buffer.from(Date.now().toString()),
|
|
91
|
+
])
|
|
92
|
+
),
|
|
93
|
+
jobType: params.jobType,
|
|
94
|
+
requester: this.publicKeyToAddress(this.keypair.publicKey),
|
|
95
|
+
description: params.description,
|
|
96
|
+
input: inputData,
|
|
97
|
+
bounty: params.bounty,
|
|
98
|
+
burnFee: 1,
|
|
99
|
+
timeout: params.timeout,
|
|
100
|
+
expectedHash: params.jobType === "deterministic" ? expectedHash : undefined,
|
|
101
|
+
timestamp: Date.now(),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return job;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
createSolution(jobId: string, output: string): Solution {
|
|
108
|
+
if (!this.keypair) {
|
|
109
|
+
throw new Error("No wallet loaded");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const outputData = this.encodeInput(output);
|
|
113
|
+
const solutionData = Buffer.concat([
|
|
114
|
+
Buffer.from(jobId, "hex"),
|
|
115
|
+
this.keypair.publicKey,
|
|
116
|
+
Buffer.from(outputData, "hex"),
|
|
117
|
+
Buffer.from(Date.now().toString()),
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
const signature = nacl.sign.detached(solutionData, this.keypair.secretKey);
|
|
121
|
+
|
|
122
|
+
const solution: Solution = {
|
|
123
|
+
id: this.hash(solutionData),
|
|
124
|
+
jobId,
|
|
125
|
+
solver: this.publicKeyToAddress(this.keypair.publicKey),
|
|
126
|
+
output: outputData,
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return solution;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
verifySolution(jobId: string, solutionId: string): VerificationResult {
|
|
135
|
+
// Simulated verification - in production this would:
|
|
136
|
+
// 1. Fetch the job from network
|
|
137
|
+
// 2. Fetch the solution from network
|
|
138
|
+
// 3. Verify signature
|
|
139
|
+
// 4. Check hash match (deterministic) or run Schelling voting (subjective)
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
valid: true,
|
|
143
|
+
reason: "Verification simulated - connect to hardclaw-node for real verification",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private publicKeyToAddress(publicKey: Uint8Array): string {
|
|
148
|
+
const hash = this.hash(Buffer.from(publicKey));
|
|
149
|
+
return `hc1${hash.slice(0, 40)}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private hash(data: Buffer): string {
|
|
153
|
+
return createHash("blake2s256").update(data).digest("hex");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private encodeInput(input: string): string {
|
|
157
|
+
// If already hex, return as-is
|
|
158
|
+
if (/^[0-9a-f]+$/i.test(input)) {
|
|
159
|
+
return input.toLowerCase();
|
|
160
|
+
}
|
|
161
|
+
// Otherwise encode as hex
|
|
162
|
+
return Buffer.from(input).toString("hex");
|
|
163
|
+
}
|
|
164
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"sourceMap": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|