safehands-pharos 1.0.2 → 1.0.3
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.
|
@@ -14,7 +14,7 @@ categories: [safety, execution, defi, intelligence]
|
|
|
14
14
|
|
|
15
15
|
SafeHands is **risk intelligence middleware** for the Pharos AI Agent economy. It sits between agent intent and on-chain execution, providing a 15-tool safety layer that any agent can compose into their workflow. Every swap, transfer, and approval flows through a 5-dimension risk assessment before touching the chain.
|
|
16
16
|
|
|
17
|
-
SafeHands is not an agent — it's
|
|
17
|
+
SafeHands is not an agent — it's the safety layer other agents depend on.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -33,7 +33,7 @@ SafeHands is not an agent — it's a **foundational primitive** that agents depe
|
|
|
33
33
|
| Tool | Description |
|
|
34
34
|
|------|-------------|
|
|
35
35
|
| `execute_swap` | Swap tokens via FaroSwap (DODO) with built-in risk gate. Automatically blocks if risk score exceeds 80. |
|
|
36
|
-
| `send_payment` | Native PHRS transfer with
|
|
36
|
+
| `send_payment` | Native PHRS transfer with risk assessment, address validation, balance checks, and high-exposure warnings. Blocks if risk score exceeds 80. |
|
|
37
37
|
| `approve_token` | ERC-20 approval for DODO router. Supports exact amounts or unlimited ("max") approval. |
|
|
38
38
|
|
|
39
39
|
### Market Intelligence — Know before you trade
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<img src="https://img.shields.io/badge/Pharos_Atlantic-688689-blueviolet?style=for-the-badge" />
|
|
5
5
|
<img src="https://img.shields.io/badge/Tools-15-orange?style=for-the-badge" />
|
|
6
6
|
<img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" />
|
|
7
|
-
<img src="https://img.shields.io/badge/
|
|
7
|
+
<img src="https://img.shields.io/badge/Security-Self--Audited-blue?style=for-the-badge" />
|
|
8
8
|
<img src="https://img.shields.io/npm/v/safehands-pharos?style=for-the-badge&logo=npm&logoColor=white&color=CB3837" />
|
|
9
9
|
</p>
|
|
10
10
|
|
|
@@ -76,6 +76,15 @@ SafeHands is an **MCP Skill** — a 15-tool risk intelligence middleware layer.
|
|
|
76
76
|
|
|
77
77
|
SafeHands is not an agent — it's the safety layer other agents depend on.
|
|
78
78
|
|
|
79
|
+
### Before vs After
|
|
80
|
+
|
|
81
|
+
| Without SafeHands | With SafeHands |
|
|
82
|
+
|---|---|
|
|
83
|
+
| Agent swaps at 12% price impact — value lost | `assess_risk` scores 87/100 — swap blocked automatically |
|
|
84
|
+
| Agent sends 950 PHRS to unverified address — funds gone | `send_payment` warns: using 95% of wallet balance |
|
|
85
|
+
| Agent wastes gas on transaction that reverts | `simulate_transaction` catches revert reason before execution |
|
|
86
|
+
| Agent B interacts with wallet Agent A flagged dangerous | `query_risk_registry` returns score 92 — interaction refused |
|
|
87
|
+
|
|
79
88
|
### Risk Engine — 5 Dimensions
|
|
80
89
|
|
|
81
90
|
| Dimension | Weight | What it checks |
|
|
@@ -92,6 +101,8 @@ SafeHands is not an agent — it's the safety layer other agents depend on.
|
|
|
92
101
|
- `61–80` → 🟠 **high** — agent warned
|
|
93
102
|
- `81–100` → 🚫 **block** — execution prevented
|
|
94
103
|
|
|
104
|
+
> To our knowledge, SafeHands is the only MCP Skill on Pharos with a built-in multi-dimension risk scoring engine and an on-chain reputation registry for cross-agent trust.
|
|
105
|
+
|
|
95
106
|
---
|
|
96
107
|
|
|
97
108
|
## 🔧 Tools (15)
|
|
@@ -321,6 +332,8 @@ contract RiskRegistry {
|
|
|
321
332
|
|
|
322
333
|
When `assess_risk` is called with a `privateKey`, the score is **automatically published** — every assessment becomes a permanent, queryable on-chain record.
|
|
323
334
|
|
|
335
|
+
> **Design note:** The RiskRegistry is intentionally permissionless — any agent can publish assessments for any wallet. This mirrors how reputation systems work in open networks: scores are weighted by who published them, not gated by who can publish. Agents should consider the `assessedBy` field when evaluating scores from unknown publishers.
|
|
336
|
+
|
|
324
337
|
---
|
|
325
338
|
|
|
326
339
|
## 🔗 Live Transaction Proof
|
|
@@ -329,20 +342,20 @@ All 15 tools tested against **Pharos Atlantic Testnet** with real on-chain trans
|
|
|
329
342
|
|
|
330
343
|
| Tool | TX Hash | Result |
|
|
331
344
|
|------|---------|--------|
|
|
332
|
-
| `execute_swap` | [`
|
|
333
|
-
| `send_payment` | [`
|
|
334
|
-
| `approve_token` | [`
|
|
335
|
-
| `publish_risk_score` | [`
|
|
345
|
+
| `execute_swap` | [`0xafc6b5da...`](https://atlantic.pharosscan.xyz/tx/0xafc6b5dac02e916466972aab41985c4609c3b5012494452047e16d33162f1c1e) | 0.01 PHRS → 0.0199 USDC |
|
|
346
|
+
| `send_payment` | [`0x58471e84...`](https://atlantic.pharosscan.xyz/tx/0x58471e84e824b84f67a239962fe82e6550f015b1a2558750232a4c4edc0e711b) | 0.001 PHRS sent |
|
|
347
|
+
| `approve_token` | [`0x12c05b61...`](https://atlantic.pharosscan.xyz/tx/0x12c05b6175d9ad0429d369a3654bc1cbd2d7d21a3b2f2342164093b1f219770e) | 100 USDC approved |
|
|
348
|
+
| `publish_risk_score` | [`0xac73e67c...`](https://atlantic.pharosscan.xyz/tx/0xac73e67c55462faefaa56b7572081a7cfcd32f6e8473b4c2f340053860df81ce) | Score 2 published |
|
|
336
349
|
| Registry deploy | [`0x7f2106b5...`](https://atlantic.pharosscan.xyz/tx/0x7f2106b5bc8c5eddcd2ea7782669ccb0b4a107da4943cffd5d49357ab5820d2e) | Contract created |
|
|
337
350
|
|
|
338
|
-
### Test Results —
|
|
351
|
+
### Test Results — 15 Tools, All Passing
|
|
339
352
|
|
|
340
353
|
```
|
|
341
354
|
# │ Status │ Tool │ Key Output
|
|
342
355
|
──────────────────────────────────────────────────────────────
|
|
343
356
|
1 │ ✅ PASS │ assess_risk │ score=2, low, proceed
|
|
344
357
|
2 │ ✅ PASS │ simulate_transaction │ wouldSucceed=true, gas=379,098
|
|
345
|
-
3 │ ✅ PASS │
|
|
358
|
+
3 │ ✅ PASS │ simulate_transaction (transfer) │ wouldSucceed=true, gas=21,000
|
|
346
359
|
4 │ ✅ PASS │ get_token_price │ PHRS price fetched
|
|
347
360
|
5 │ ✅ PASS │ get_wallet_balance │ PHRS=19.41, USDC=0.85
|
|
348
361
|
6 │ ✅ PASS │ check_allowance │ needsApproval=true
|
|
@@ -362,13 +375,13 @@ All 15 tools tested against **Pharos Atlantic Testnet** with real on-chain trans
|
|
|
362
375
|
|
|
363
376
|
## 🔒 Security
|
|
364
377
|
|
|
365
|
-
|
|
378
|
+
Security self-audit — compliant with Pharos Skill Scanner guidelines:
|
|
366
379
|
|
|
367
380
|
- ✅ **Private keys never stored** — passed per-request, never logged or persisted
|
|
368
381
|
- ✅ **No hardcoded secrets** — all sensitive config via `.env`
|
|
369
382
|
- ✅ **No shell execution** — pure TypeScript with viem RPC calls
|
|
370
383
|
- ✅ **No file system abuse** — no local file reads/writes during tool execution
|
|
371
|
-
- ✅ **Risk gating on
|
|
384
|
+
- ✅ **Risk gating on execution** — `execute_swap` and `send_payment` run full risk assessment before execution. All write tools enforce safety checks before touching the chain.
|
|
372
385
|
- ✅ **Automatic blocking** — score > 80 halts execution, no silent failures
|
|
373
386
|
|
|
374
387
|
---
|
|
@@ -380,11 +393,11 @@ CertiK Skill Scanner compliant:
|
|
|
380
393
|
- [x] 5-dimension risk scoring engine
|
|
381
394
|
- [x] FaroSwap (DODO) integration
|
|
382
395
|
- [x] On-chain RiskRegistry smart contract
|
|
383
|
-
- [x]
|
|
396
|
+
- [x] 15/15 integration tests passing on Pharos Atlantic
|
|
384
397
|
|
|
385
398
|
### Phase 2 — NPM Package
|
|
386
|
-
- [
|
|
387
|
-
- [
|
|
399
|
+
- [x] Publish `safehands-pharos` to npm
|
|
400
|
+
- [x] One-line install: `npx safehands-pharos`
|
|
388
401
|
- [ ] Configurable risk thresholds per-agent
|
|
389
402
|
|
|
390
403
|
### Phase 3 — Agent Arena
|
|
@@ -50,6 +50,24 @@ export declare function handleSendPayment(input: SendPaymentInput): Promise<{
|
|
|
50
50
|
warnings: string[];
|
|
51
51
|
};
|
|
52
52
|
error: string;
|
|
53
|
+
riskAssessment?: undefined;
|
|
54
|
+
txHash?: undefined;
|
|
55
|
+
explorerUrl?: undefined;
|
|
56
|
+
amountSent?: undefined;
|
|
57
|
+
gasUsed?: undefined;
|
|
58
|
+
} | {
|
|
59
|
+
success: boolean;
|
|
60
|
+
validation: {
|
|
61
|
+
addressValid: boolean;
|
|
62
|
+
balanceSufficient: boolean;
|
|
63
|
+
warnings: string[];
|
|
64
|
+
};
|
|
65
|
+
riskAssessment: {
|
|
66
|
+
riskScore: number;
|
|
67
|
+
wasBlocked: boolean;
|
|
68
|
+
blockReason: string;
|
|
69
|
+
};
|
|
70
|
+
error: string;
|
|
53
71
|
txHash?: undefined;
|
|
54
72
|
explorerUrl?: undefined;
|
|
55
73
|
amountSent?: undefined;
|
|
@@ -65,6 +83,11 @@ export declare function handleSendPayment(input: SendPaymentInput): Promise<{
|
|
|
65
83
|
balanceSufficient: boolean;
|
|
66
84
|
warnings: string[];
|
|
67
85
|
};
|
|
86
|
+
riskAssessment: {
|
|
87
|
+
riskScore: number;
|
|
88
|
+
wasBlocked: boolean;
|
|
89
|
+
blockReason?: undefined;
|
|
90
|
+
};
|
|
68
91
|
error?: undefined;
|
|
69
92
|
}>;
|
|
70
93
|
//# sourceMappingURL=sendPayment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendPayment.d.ts","sourceRoot":"","sources":["../../src/tools/sendPayment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"sendPayment.d.ts","sourceRoot":"","sources":["../../src/tools/sendPayment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;EAM5B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEjE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CAI3B,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoF9D"}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { publicClient, createPharosWalletClient, getExplorerUrl } from "../lib/pharosClient.js";
|
|
4
4
|
import { isAddress, parseEther, formatEther } from "viem";
|
|
5
|
-
import {
|
|
5
|
+
import { assessRisk } from "../lib/riskEngine.js";
|
|
6
|
+
import { MAX_BALANCE_USAGE_PCT, RISK_BLOCK_THRESHOLD } from "../lib/constants.js";
|
|
6
7
|
export const sendPaymentSchema = z.object({
|
|
7
8
|
toAddress: z.string(),
|
|
8
9
|
amount: z.string(),
|
|
@@ -45,6 +46,29 @@ export async function handleSendPayment(input) {
|
|
|
45
46
|
if (usagePct > MAX_BALANCE_USAGE_PCT) {
|
|
46
47
|
warnings.push(`Using ${usagePct}% of wallet balance — high exposure`);
|
|
47
48
|
}
|
|
49
|
+
// Risk assessment
|
|
50
|
+
const risk = await assessRisk({
|
|
51
|
+
action: "transfer",
|
|
52
|
+
amount: input.amount,
|
|
53
|
+
toAddress: input.toAddress,
|
|
54
|
+
walletAddress: input.walletAddress,
|
|
55
|
+
});
|
|
56
|
+
if (risk.riskScore > RISK_BLOCK_THRESHOLD) {
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
validation: {
|
|
60
|
+
addressValid: true,
|
|
61
|
+
balanceSufficient: true,
|
|
62
|
+
warnings,
|
|
63
|
+
},
|
|
64
|
+
riskAssessment: {
|
|
65
|
+
riskScore: risk.riskScore,
|
|
66
|
+
wasBlocked: true,
|
|
67
|
+
blockReason: risk.suggestion,
|
|
68
|
+
},
|
|
69
|
+
error: `Payment blocked — risk score ${risk.riskScore}/100: ${risk.suggestion}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
48
72
|
try {
|
|
49
73
|
const wallet = createPharosWalletClient(input.privateKey);
|
|
50
74
|
const txHash = await wallet.sendTransaction({
|
|
@@ -59,6 +83,10 @@ export async function handleSendPayment(input) {
|
|
|
59
83
|
amountSent: input.amount,
|
|
60
84
|
gasUsed: receipt.gasUsed.toString(),
|
|
61
85
|
validation,
|
|
86
|
+
riskAssessment: {
|
|
87
|
+
riskScore: risk.riskScore,
|
|
88
|
+
wasBlocked: false,
|
|
89
|
+
},
|
|
62
90
|
};
|
|
63
91
|
}
|
|
64
92
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sendPayment.js","sourceRoot":"","sources":["../../src/tools/sendPayment.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"sendPayment.js","sourceRoot":"","sources":["../../src/tools/sendPayment.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED