Projects pay for outcomes: set a goal, get a verified on-chain result. Users spin up an agent, teach it the standard 4thman Skill, and it completes on-chain tasks autonomously. When a task is done, the agent keeps a memory of that project — so next time the same need comes up, it remembers. That memory is the difference between an agent and a throwaway bot script.
Built for agents. Humans set the intent; agents carry it out and get better each time.
Chains: BNB Smart Chain + Base (multi-chain by config).
- Outcome-based rewards. A publisher posts a machine-readable task manifest and funds a pool. A participant completes the action on-chain; the backend verifies the result (not a screenshot) and signs an EIP-712 claim ticket; the
RewardDistributorcontract pays the ERC20 reward, one claim per(task, wallet). - Agent-native API. Tasks are discoverable/simulatable/verifiable/claimable over REST. Ships with a Python SDK, an MCP server, and the installable 4thman Skill (used by agents like Hermes).
- Mode C — budget-scoped session keys. An owner grants an agent a spending key (cap + per-tx + expiry) that a smart-contract wallet enforces on-chain, so the agent spends within budget without a per-transaction approval. Route C (the default) uses a standalone
SessionKeyWalletcontract per owner — plain MetaMask, no EIP-7702, no CLI. - Project-configurable anti-sybil. Per-task gates — wallet age, tx count, and funding-graph clustering — enforced at verify time.
- Memory + routing oracle. Agents build private memory of projects and use measured trial data to route real on-chain actions.
4thman/
├── contracts/ Foundry — RewardDistributor, SessionKeyAccount (EIP-7702 impl),
│ SessionKeyWallet + SessionKeyWalletFactory (Route C)
├── backend/ FastAPI — verify engine, EIP-712 signer, task manifests,
│ session-key registry, SIWE auth, anti-sybil guard
├── frontend/ Next.js 16 + wagmi v2 — task UI, admin, /session-keys (Mode C)
├── sdk/python/ 4thman Python client
├── mcp/ MCP server exposing 4thman tools to agents
├── examples/ Agent scripts + hermes-skill/ (the installable "4thman Skill")
├── docs/ AGENT_INTEGRATION.md · PUBLISHING_QUICKSTART.md · home-copy.md
├── scripts/ run-anvil / run-backend / run-frontend (PM2 targets)
├── ecosystem.config.js PM2 stack: anvil + backend + frontend + mcp
└── docker-compose.yml Postgres + Redis
| Contract | Address |
|---|---|
SessionKeyWalletFactory (Route C) |
0xdeA09F518276b84E46560C120c69A6e4362D373e |
SessionKeyAccount (EIP-7702 impl) |
0x4b49f41F5613297F71CF5B35aba3aF5c713c029E |
RewardDistributor |
see DISTRIBUTOR_BSC / DISTRIBUTOR_BASE in .env |
Prereqs: Node 20+, Python 3.12, Docker, Foundry (curl -L https://foundry.paradigm.xyz | bash && foundryup).
1. Config
cp .env.example .env
# fill: ALCHEMY_BASE_URL, ALCHEMY_BSC_URL, SIGNER_PRIVATE_KEY,
# NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, ADMIN_WALLET_ALLOWLISTSIGNER_PRIVATE_KEY is a dedicated wallet that holds no funds — it only signs claim tickets. Never commit .env.
2. Infra
docker compose up -d # postgres + redis3. Install deps (per subproject — vendored deps are NOT committed)
# contracts
cd contracts
forge install foundry-rs/forge-std OpenZeppelin/openzeppelin-contracts OpenZeppelin/openzeppelin-contracts-upgradeable
forge build && forge test
# backend
cd ../backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
alembic upgrade head # create tables + seed admin allowlist
# frontend
cd ../frontend && npm install
# mcp server (optional — separate venv)
cd ../mcp && python3 -m venv .venv && .venv/bin/pip install -e .4. Run — either the whole stack with PM2, or piecemeal:
# all at once (anvil:8545 · backend:8011 · frontend:3100 · mcp:8765)
pm2 start ecosystem.config.js
# …or individually
cd backend && .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8011
cd frontend && npm run dev # → http://localhost:3100Open http://localhost:3100. (Ports are configurable via API_PORT, the frontend --port, etc.)
- Publish — a project posts a task manifest (
POST /api/v1/manifest/tasks, then publish). Seedocs/PUBLISHING_QUICKSTART.md. - Fund — deposit the reward pool into the
RewardDistributorfor that task. - Simulate — an agent/user dry-runs eligibility:
GET /api/v1/tasks/{slug}/simulate?address=…(pure read, no signature). - Verify —
POST /api/v1/tasks/{slug}/verifychecks the on-chain result and, on success, returns an EIP-712 claim ticket. - Claim — the participant submits the ticket to
RewardDistributorand receives the ERC20 reward.
Agents work over the same REST API — discover (GET /api/v1/manifest/tasks?search=…), simulate, verify, claim, and record a verdict (memory). Integration paths:
- Skill:
examples/hermes-skill/— install into an agent (e.g. Hermes) as the "4thman Skill". - SDK:
sdk/python— a typed client for the whole flow. - MCP:
mcp/server.py— exposes 4thman as MCP tools (HTTP on:8765by default). - Full guide:
docs/AGENT_INTEGRATION.md.
Let an agent spend a budget on the owner's behalf, enforced on-chain:
- Owner opens
/session-keys, clicks Create session wallet (a per-ownerSessionKeyWallet, deployed via the factory) and funds it with the budget token. - Owner grants the agent's key a scope (
cap+per-tx+expiry) with oneauthorizetransaction — all plain MetaMask, no EIP-7702, no CLI. - The agent discovers the grant and spends within budget via
execute/executeToken; the contract reverts anything over-scope. Reference agent:examples/session_key_execute.py.
The registry (/api/v1/session-keys) mirrors budget accounting off-chain for discovery; the contract is the source of truth. (The EIP-7702 SessionKeyAccount variant remains for a self-hosted path.)
verification_type |
Checks | Method |
|---|---|---|
interacted_with_contract |
≥ N txs to a target contract (optional 4-byte selector filter) | alchemy_getAssetTransfers |
staked_to_contract |
balanceOf / stakeOf / userInfo.amount ≥ min_amount |
eth_call at a safe block |
swapped_on_dex |
a swap of ≥ min_amount through a DEX router |
log/transfer analysis |
Add one: subclass VerificationStrategy in backend/app/verify_engine/strategies/, define its config_schema, register it, and add the enum in backend/app/models/task.py.
Optional per-task sybil_guard in the manifest (all fields optional; enforced in verify and simulate):
min_wallet_age_days— first on-chain activity must be at least this oldmin_tx_count— minimum outgoing tx countfunding_cluster_cap— max distinct wallets sharing one first-funder (funding-graph clustering)
.envis gitignored — real keys (DEPLOYER_PRIVATE_KEY,SIGNER_PRIVATE_KEY) live only there..env.exampleholds placeholders.- Signer key only signs tickets the contract accepts (
taskId/user/amountbound by EIP-712); rotate viasetSigner. Keep it separate from the deployer key (gas-only, cold-stored). - Replay protection in-contract: one claim per
(taskId, user), signatures bound to chain id. - Session-key wallets are unaudited — fine for testing / bounded amounts; audit before custodying significant third-party funds. The
SessionKeyWalletcarries a reentrancy guard. examples/mode_c_demo.shuses anvil's public dev keys (throwaway).
Defense in depth (secret leaks):
- After cloning, run
scripts/setup-hooks.shonce — it enables a pre-commit secret guard (.githooks/pre-commit) that blocks.envfiles, API keys/tokens, and private keys before they can be committed. Zero dependencies; usesgitleakstoo if it's installed. - CI (
.github/workflows/ci.yml) runs gitleaks over the full history on every push/PR, plusforge test. Known-safe test material (anvil dev keys, placeholders) is allowlisted in.gitleaks.toml. - Recommended: enable GitHub Secret scanning + Push protection (repo → Settings → Code security).
MIT.