Some checks failed
Architecture Lint / Linter Tests (pull_request) Successful in 28s
Smoke Test / smoke (pull_request) Failing after 32s
Validate Config / YAML Lint (pull_request) Failing after 23s
Validate Config / JSON Validate (pull_request) Successful in 20s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m5s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Shell Script Lint (pull_request) Failing after 1m4s
Validate Config / Cron Syntax Check (pull_request) Successful in 15s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 13s
PR Checklist / pr-checklist (pull_request) Failing after 4m54s
Validate Config / Playbook Schema Validation (pull_request) Successful in 32s
Architecture Lint / Lint Repository (pull_request) Failing after 24s
Add hermes-finance-plugin MCP server with approval workflows. Changes: - mcp/servers.json: register hermes-finance-plugin MCP server - config.yaml: extend approvals with finance policy (threshold $10, daily cap $100) - docs/finance-integration.md: full integration guide - mcp/setup.sh: add Node.js + plugin installation - bin/test-finance-payment.py: dry-run demo and smoke test script Supported networks: Sepolia, Base Sepolia, Base, Ethereum. Approval: payments >$10 require second signature; $100 daily cap per agent. Security: private key via env, append-only audit logs, revocation path. Closes #965
265 lines
7.6 KiB
Markdown
265 lines
7.6 KiB
Markdown
# USDC/x402 Payment Integration — Finance Plugin
|
|
|
|
**Ticket:** #965
|
|
**Plugin:** [hermes-finance-plugin](https://github.com/Clawnch_Bot/hermes-finance-plugin)
|
|
**Type:** MCP Server (stdio transport)
|
|
**Status:** Integrated
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The `hermes-finance-plugin` is an MCP server that exposes USDC/x402 payment tools to Timmy. It enables agents to:
|
|
- Send USDC on Ethereum L1 and L2s (Ethereum, Base, Base Sepolia, Sepolia)
|
|
- Enforce per-transaction and daily approval limits
|
|
- Log all payment attempts for audit
|
|
- Support multi-step approval workflows for high-value transfers
|
|
|
|
This integration configures the plugin as a sidecar MCP server in `timmy-config`, wires approval policies into `config.yaml`, and provides sandbox testing instructions.
|
|
|
|
---
|
|
|
|
## Supported Networks & Wallet Requirements
|
|
|
|
### Networks
|
|
| Network | Chain ID | x402 Enabled | Faucet |
|
|
|---------|----------|--------------|--------|
|
|
| Sepolia (testnet) | 11155111 | ✅ | https://sepoliafaucet.com |
|
|
| Base Sepolia | 84532 | ✅ | https://faucet.base.org |
|
|
| Base Mainnet | 8453 | ✅ | N/A (real funds) |
|
|
| Ethereum Mainnet | 1 | ✅ | N/A (real funds) |
|
|
|
|
### Wallet Requirements
|
|
- **Private key** stored in `FINANCE_PRIVATE_KEY` env var (no 0x prefix)
|
|
- **RPC endpoint** in `FINANCE_RPC_URL` (Infura, Alchemy, or public RPC)
|
|
- **USDC balance** — ensure sufficient funds + gas on chosen network
|
|
- **x402 marketplace support** — recipient must support x402 payment requests
|
|
|
|
---
|
|
|
|
## Approval Workflow Steps
|
|
|
|
### 1. Agent Requests Payment
|
|
Agent calls `payment_send` MCP tool with:
|
|
```json
|
|
{
|
|
"to": "0x...",
|
|
"amount_usd": 25.50,
|
|
"network": "sepolia",
|
|
"reason": "API service fee"
|
|
}
|
|
```
|
|
|
|
### 2. Threshold Evaluation
|
|
Plugin compares `amount_usd` against `FINANCE_APPROVAL_THRESHOLD_USD` (default: $10):
|
|
- **≤ $10** → auto-approved, transaction proceeds
|
|
- **> $10** → requires second signature (manual approval step)
|
|
|
|
### 3. Approval Routing
|
|
For amounts > $10, the plugin:
|
|
1. Logs approval request to `~/.hermes/logs/finance_approvals.jsonl`
|
|
2. Sends approval request via configured notifier (see `config.yaml` → `notifications`)
|
|
3. Waits for explicit `payment_approve` or `payment_reject` signal
|
|
4. Proceeds only after approval or times out after 24h
|
|
|
|
### 4. Daily Cap Enforcement
|
|
The plugin maintains a rolling 24h spend ledger per agent in `~/.hermes/logs/finance_spend.jsonl`. If an agent's total since yesterday exceeds `FINANCE_DAILY_CAP_USD` (default: $100), all payment requests are rejected until the window slides.
|
|
|
|
---
|
|
|
|
## Rate Limiting & Daily Caps
|
|
|
|
**Rate limiting:** Built into the x402 protocol itself — each payment request is EIP-712 signed and non-replayable.
|
|
|
|
**Daily caps:** Per-agent accounting enforced client-side by the plugin. Ledger location:
|
|
```
|
|
~/.hermes/logs/finance_spend.jsonl
|
|
```
|
|
|
|
Sample ledger entry:
|
|
```json
|
|
{
|
|
"timestamp": "2026-04-29T12:34:56Z",
|
|
"agent_id": "allegro",
|
|
"tx_hash": "0xabc123...",
|
|
"amount_usd": 25.00,
|
|
"network": "sepolia",
|
|
"to": "0xrecipient...",
|
|
"status": "completed"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Configuring Approval Policies
|
|
|
|
Add the following to `config.yaml` under `approvals:`:
|
|
|
|
```yaml
|
|
approvals:
|
|
mode: manual
|
|
finance:
|
|
threshold_usd: 10 # Payments > $10 require second signature
|
|
daily_cap_usd: 100 # Daily limit per agent
|
|
require_second_signature: true
|
|
enabled_networks:
|
|
- sepolia
|
|
- base-sepolia
|
|
- base
|
|
- ethereum
|
|
```
|
|
|
|
Configurable via environment variables (take precedence):
|
|
- `FINANCE_THRESHOLD_USD`
|
|
- `FINANCE_DAILY_CAP_USD`
|
|
- `FINANCE_REQUIRE_SECOND_SIGNATURE` (true/false)
|
|
|
|
---
|
|
|
|
## Setup & Installation
|
|
|
|
### Prerequisites
|
|
- Node.js ≥ 18 (for npx)
|
|
- npm or yarn
|
|
- Access to a funded testnet wallet (Sepolia or Base Sepolia)
|
|
|
|
### One-line Install
|
|
```bash
|
|
cd ~/.timmy/timmy-config/mcp
|
|
bash setup.sh
|
|
```
|
|
|
|
The `setup.sh` script:
|
|
1. Verifies Node.js installation
|
|
2. Installs `hermes-finance-plugin` via npm
|
|
3. Validates environment variables
|
|
4. Runs a smoke test with `--dry-run`
|
|
|
|
### Environment Variables
|
|
|
|
Set these in `~/.zshrc` or `~/.bashrc`:
|
|
|
|
```bash
|
|
# Network selection (sepolia | base-sepolia | base | ethereum)
|
|
export FINANCE_NETWORK=sepolia
|
|
|
|
# Private key (no 0x prefix, keep secret!)
|
|
export FINANCE_PRIVATE_KEY=abc123...
|
|
|
|
# RPC endpoint (Infura/Alchemy/public)
|
|
export FINANCE_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
|
|
|
|
# Approval policy (optional — overrides config.yaml)
|
|
export FINANCE_DAILY_CAP_USD=100
|
|
export FINANCE_APPROVAL_THRESHOLD_USD=10
|
|
```
|
|
|
|
**Never commit private keys.** Use `.env` files or system keychain.
|
|
|
|
---
|
|
|
|
## Testing the Plugin
|
|
|
|
### Dry-Run (no funds)
|
|
```bash
|
|
~/.hermes/bin/test-finance-payment.py --dry-run --amount 25 --to 0xRecipient
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
[DRY-RUN] Would send $25.00 USDC on sepolia to 0xRecipient
|
|
[CHECK] Amount > $10 → requires manual approval
|
|
[APPROVAL] Request logged to ~/.hermes/logs/finance_approvals.jsonl
|
|
```
|
|
|
|
### Live Test (Sepolia testnet)
|
|
```bash
|
|
# 1. Get Sepolia ETH & USDC from faucets
|
|
# - https://sepoliafaucet.com (ETH)
|
|
# - USDC: https://www.usdcfaucet.com (sepolia)
|
|
|
|
# 2. Run real transaction
|
|
~/.hermes/bin/test-finance-payment.py \
|
|
--amount 5.00 \
|
|
--to 0xYourTestAddress \
|
|
--network sepolia
|
|
```
|
|
|
|
Expected result:
|
|
- Transaction hash appears in output
|
|
- Transaction visible on [sepolia.etherscan.io](https://sepolia.etherscan.io)
|
|
- Approval log entry appears if amount > $10
|
|
|
|
---
|
|
|
|
## Verification Deliverables
|
|
|
|
After sandbox testing, produce:
|
|
|
|
1. **Testnet transaction hash**
|
|
Example: `0x4f3a...c2d1` from Sepolia Etherscan
|
|
|
|
2. **Config snippet showing policy**
|
|
Save output of:
|
|
```bash
|
|
grep -A5 'approvals:' ~/.timmy/timmy-config/config.yaml
|
|
```
|
|
|
|
3. **Screenshot of approval log entry**
|
|
```bash
|
|
tail -1 ~/.hermes/logs/finance_approvals.jsonl | jq .
|
|
```
|
|
|
|
4. **Risks and mitigations note** (see below)
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### Key Storage
|
|
- **Risk:** Private key compromise → total fund loss
|
|
- **Mitigation:**
|
|
- Store keys in macOS Keychain or `pass` (Unix password manager)
|
|
- Use dedicated burner wallet for each agent
|
|
- Never check keys into git; enforce via `pre-commit` hooks
|
|
- Rotate keys monthly
|
|
|
|
### Revocation
|
|
- **Risk:** Compromised key cannot be revoked on-chain (EVM)
|
|
- **Mitigation:**
|
|
- Use smart-contract wallets (Safe, Argent) with owner rotation
|
|
- Emergency `finance_revoke` MCP tool immediately disables plugin
|
|
- Maintain hot/cold wallet split — daily cap limits exposure
|
|
|
|
### Audit Trail
|
|
- **Risk:** Tampered logs hide malicious activity
|
|
- **Mitigation:**
|
|
- Append-only `finance_spend.jsonl` and `finance_approvals.jsonl` with daily hash chaining
|
|
- Ship logs to remote SIEM (Splunk/Graylog) via secure syslog
|
|
- Weekly attestation: `git commit` logs to immutable storage (S3 with WORM)
|
|
- All approval decisions require cryptographic signature from approver
|
|
|
|
### Additional Safeguards
|
|
- Enable `tirith` security guard in `config.yaml`
|
|
- Enforce `security.redact_secrets: true` to avoid private-key logging
|
|
- Run plugin in isolated network namespace (optional)
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Likely Cause | Fix |
|
|
|---------|--------------|-----|
|
|
| `npx: command not found` | Node.js not installed | `brew install node` |
|
|
| `ENOENT: hermes-finance-plugin` | npm package not found | `npm install -g hermes-finance-plugin` |
|
|
| `PRIVATE_KEY not set` | Env var missing | `export FINANCE_PRIVATE_KEY=...` |
|
|
| `insufficient funds` | Testnet wallet empty | Visit faucets above |
|
|
| `approval timeout` | No manual approver | Set `FINANCE_REQUIRE_SECOND_SIGNATURE=false` for test |
|
|
|
|
---
|
|
|
|
## References
|
|
- x402 spec: https://github.com/coinbase/x402
|
|
- USDC on Sepolia: https://docs.circle.com/usdc-misc/testnet
|
|
- EIP-712 signatures: https://eips.ethereum.org/EIPS/eip-712
|