SettleProof verifies whether an expected crypto payment is safe for a business to act on. The system exists to avoid a single RPC provider becoming the only source of truth for fulfillment, access control, account crediting, or invoice closure.
A merchant needs one operational answer: can this payment be treated as paid?
Relying on one RPC provider creates avoidable failure modes:
- The provider is unavailable, so paid customers look unpaid.
- The provider is stale, so confirmations appear later than they should.
- The provider returns bad or inconsistent data, so an unpaid invoice may look paid.
- Support and ops fall back to manual block explorer checks.
SettleProof reduces that risk by checking multiple RPC providers and recording the evidence behind the final task status.
graph TD
Merchant["Merchant system"] -->|POST /v1/tasks| API["SettleProof API"]
API --> Store["SQLite task store"]
Worker["Verification worker"] --> Store
Worker --> RPC1["RPC provider A"]
Worker --> RPC2["RPC provider B"]
Worker --> RPC3["RPC provider C"]
RPC1 --> Quorum["Quorum evaluator"]
RPC2 --> Quorum
RPC3 --> Quorum
Quorum --> Store
Store --> Callback["Callback delivery"]
Callback -->|status update| Merchant
The MVP should keep the system small:
- REST intake for verification tasks.
- SQLite persistence for tasks, observations, status history, and callback attempts.
- Multi-RPC quorum verification for ERC20 transfers.
- At-least-once callback delivery when task status changes.
- Local devnet proof using Anvil and simulated RPC provider failures.
Implemented intake endpoints:
GET /health: returns API and SQLite health.POST /v1/tasks: validates and persists an ERC20 transfer verification task.GET /v1/tasks/{task_id}: returns the stored task state.
POST /v1/tasks accepts:
{
"type": "erc20_transfer",
"chain_id": 31337,
"token_address": "0x0000000000000000000000000000000000000001",
"from_address": "0x0000000000000000000000000000000000000002",
"to_address": "0x0000000000000000000000000000000000000003",
"amount": "1000000",
"from_block": 1,
"min_confirmations": 12,
"callback_url": "http://merchant.local/callback",
"expires_after_blocks": 1000
}Notes:
task_idis optional. If omitted, SettleProof generates one.Idempotency-Keyis optional but recommended for merchant retries.- A repeated request with the same idempotency key and same body returns the existing task.
- A repeated idempotency key or task id with different request content returns
409 Conflict. - New tasks start as
pending; the verification round reads pending tasks from SQLite in oldest-first batches.
- A merchant submits the expected payment and a callback URL.
- SettleProof stores the task and returns a task id.
- The verifier queries configured RPC providers for latest block and matching ERC20 transfer logs.
- A payment is confirmed only when the configured quorum agrees on the same transaction hash, block number, and log index with enough confirmations.
- SettleProof records the status transition, checked provider count, quorum provider count, and settlement evidence.
- Callback delivery is still stubbed; durable delivery is the next MVP gap.
The core invariant is:
SettleProof must not mark a payment confirmed from one RPC provider alone.
Recommended task statuses for the MVP:
pending: task accepted and waiting for enough evidence.confirmed: quorum agreed on a matching transaction with enough confirmations.conflict: providers returned incompatible confirmed evidence.ambiguous: multiple matching events satisfy the task and the task needs narrower input.expired: the task passed its configured block/time limit without confirmation.callback_failed: the task status changed, but callback delivery reached its retry limit.
This repository started as a hackathon demo built on Open Autonomy/AEA. The
current code has a REST task intake path backed by SQLite. The agent reads
pending SQLite tasks, checks configured RPC providers, and marks ERC20 transfer
tasks confirmed only when quorum agrees on the same log identity.
Known gaps before the MVP claim is true:
- Webhook delivery is currently stubbed and needs retries plus durable attempt records.
- The local proof should move from public fork/manual setup to an Anvil-based devnet test.
- Provider observations are summarized on the task row; a dedicated observation table would make audits and debugging stronger.
- Python
>=3.10 - Poetry
- Docker Engine and Docker Compose
- Tendermint
0.34.19 - IPFS node
0.6.0
Create a virtual environment and install dependencies:
poetry shell
poetry installSync Open Autonomy packages:
autonomy packages sync --update-packagesRun one local agent after creating .env and key files:
bash run_agent.shRun the four-agent service after preparing .env and keys.json:
bash run_service.shThe productized test path should avoid mainnet and public testnets:
- Start Anvil locally.
- Deploy a minimal ERC20 test token.
- Expose three RPC provider facades that can simulate healthy, down, lagging, and corrupt provider behavior.
- Submit a SettleProof task through the REST API.
- Send a matching ERC20 transfer on Anvil.
- Assert the task reaches
confirmedonly when quorum agrees. - Assert callbacks are retried and recorded when the receiver fails.