Skip to content

edycutjong/payguard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

24 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PayGuard

PayGuard πŸ’‚

The CertiK-grade seatbelt for x402 β€” safe, guarded agent payments on Pharos.

Agent Skill Demo Video On-chain Settled Pharos Hackathon



Solidity TypeScript x402 tested with Foundry License: MIT CI CodeQL


πŸš€ Run it β€” for judges

Verify the core claims in 30 seconds β€” zero keys, zero accounts, fully offline:

npm install && npm run bench                                         # β†’ 8/8 attacks blocked
forge install foundry-rs/forge-std OpenZeppelin/openzeppelin-contracts && forge test   # β†’ 21/21 + fuzz + reentrancy
npm run agent                                                        # β†’ autonomous agent; GuardianRail blocks 5/5 unsafe buys (offline, no key)

Want the live on-chain settlement too? It's a real testnet tx you can verify without running anything β€” see Phase 1 on-chain proof. To reproduce it locally (needs a funded Atlantic test wallet):

cp .env.example .env          # fill AGENT_PK, FACILITATOR_PK, RECEIVER_ADDRESS (see comments)
forge script script/DeployMockUSDC.s.sol --rpc-url atlantic --broadcast   # prints MockUSDC β†’ set USDC_ADDRESS
npm run probe                 # prove EIP-3009 settlement on Atlantic
npm run server                # terminal 1 β€” x402-protected resource server
npm run demo                  # terminal 2 β€” guarded agent pays end-to-end (settles 0.001 USDC)

πŸ“Έ See it in action

PayGuard β€” GuardianRail blocks 8/8 agent-drain attacks and settles on Pharos

GuardianRail faces 8 attack vectors β€” every unauthorized spend is blocked before the agent ever signs:

  πŸ›‘οΈ  PayGuard Β· GuardianRail β€” Attack β†’ Blocked
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (index) β”‚ Attack Vector                   β”‚ Result          β”‚ Guard Code          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0       β”‚ 1. The Drainer (10,000 USDC)    β”‚ πŸ›‘ BLOCKED      β”‚ MAX_SPEND           β”‚
β”‚ 1       β”‚ 2. The Phish (0xFakeToken)      β”‚ πŸ›‘ BLOCKED      β”‚ INVALID_ASSET       β”‚
β”‚ 2       β”‚ 3. The Rogue Node (bad payTo)   β”‚ πŸ›‘ BLOCKED      β”‚ UNAPPROVED_PAYEE    β”‚
β”‚ 3       β”‚ 4. The Revert (sim fails)       β”‚ πŸ›‘ BLOCKED      β”‚ SIMULATION_FAILED   β”‚
β”‚ 4       β”‚ 5. The Inflator (negative amt)  β”‚ πŸ›‘ BLOCKED      β”‚ MAX_SPEND           β”‚
β”‚ 5       β”‚ 6. The Malformed (float amt)    β”‚ πŸ›‘ BLOCKED      β”‚ INVALID_ASSET       β”‚
β”‚ 6       β”‚ 7. The Clean Run                β”‚ βœ… AUTHORIZED   β”‚ AUTHORIZED          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  βœ… TOC/TOU pin β€” 2-entry offer collapsed to the single validated requirement
  βœ… 8/8 vectors handled correctly β€” 100% of unauthorized agent spends blocked.

πŸ€– Skill-to-Agent β€” the guard protecting a live agent

npm run agent turns GuardianRail loose on an autonomous agent. A procurement agent is given a budget and a goal ("assemble a trading-research data bundle") and shops an x402 data marketplace β€” every purchase gated by the shipped createGuardedFetch before any signature. Set ANTHROPIC_API_KEY for a live Claude (claude-opus-4-8) tool-use loop; with no key it runs a deterministic planner (offline, identical every run):

πŸ€– autonomous agent Β· goal: assemble a safe research bundle under 5 USDC/day
   βœ… DataHub       AUTHORIZED β€” charged 0.5 USDC
   πŸ›‘ AlphaDrainer  BLOCKED [MAX_SPEND]          10,000 USDC > 5 USDC per-call cap
   βœ… WeatherWire   AUTHORIZED β€” charged 2 USDC
   πŸ›‘ CheapFeed     BLOCKED [INVALID_ASSET]      spoofed token, not USDC
   πŸ›‘ GhostNode     BLOCKED [UNAPPROVED_PAYEE]   payee not in allowlist
   πŸ›‘ PausedOracle  BLOCKED [SIMULATION_FAILED]  eth_call would revert
   πŸ›‘ NewsFeed      BLOCKED [BUDGET_EXCEEDED]    3 USDC > 2.5 USDC left today
  πŸ’° spent 2.5 / 5 USDC Β· 5 unsafe payments blocked β€” every drain, spoof, rogue-payee,
     revert, and over-budget attempt stopped BEFORE the agent ever signs.

It reuses the shipped guard unchanged β€” the real GuardianRail protecting a real agent loop, not a reimplementation. The EIP-3009 authorization for a blocked buy is never created.

πŸ’‘ The problem & solution

x402 is Pharos's flagship agent-payment rail β€” but it signs and settles whatever a server's 402 PAYMENT-REQUIRED demands. One LLM hallucination or one rogue endpoint can drain an agent's wallet in seconds: no spend caps, no simulation, no asset checks, no escrow.

PayGuard is the safety layer x402 is missing β€” two composable, atomic Skills any Pharos agent imports:

  • 🚦 GuardianRail β€” a pre-flight interceptor that enforces spend caps, asset-spoof protection, payee allowlists, and an eth_call simulation before the agent signs the EIP-3009 authorization.
  • 🏦 AgentVault β€” a minimal, non-upgradeable, CertiK-grade USDC escrow for conditional / milestone payments.
  • πŸ›°οΈ Proven end-to-end β€” a guarded agent really settles USDC on Pharos Atlantic (verifiable tx below).

πŸ› οΈ The two Skills

🚦 Tool 1 β€” GuardianRail (pre-flight payment interceptor)

Nests under the x402 client via createGuardedFetch, so it gates the 402 offer before signing:

const guarded  = createGuardedFetch(fetch, policy, { rpcUrl });
const safeFetch = wrapFetchWithPayment(guarded, client);   // throws AgentSecurityError on violation

Enforces: canonical-asset (anti-spoof) Β· per-call cap Β· daily budget Β· payee allowlist Β· eth_call simulation.

🏦 Tool 2 β€” AgentVault (conditional USDC escrow)

Minimal, non-upgradeable, strict-CEI escrow (SafeERC20 + ReentrancyGuard) for milestone payments: lock(payee, amount, conditionHash, deadline) β†’ release(id, preimage) / refund(id).

πŸ—οΈ Architecture (Hybrid facilitator β€” "Option 3")

graph TD
    Agent[Agent] --> Guard["createGuardedFetch(policy)<br/>enforce + eth_call sim (pre-signature)"]
    Guard --> Client["x402 client (EIP-3009 sign)"]
    Guard --> Server["x402 server (/api/data, 402)"]
    Server --> Fac[Facilitator]
    Fac --> Def["default: official Pharos FACILITATOR_URL"]
    Fac --> Fall["fallback: in-process x402Facilitator (self-hosted)"]
    Def --> Settles["settles USDC on Atlantic (688689)"]
    Fall --> Settles
    AV["High-value path: AgentVault.sol"] --> Actions["lock β†’ release(proof) / refund(deadline)"]
Loading

The in-process facilitator implements FacilitatorClient directly, so the self-hosted fallback needs no HTTP-facilitator service β€” the demo is deterministic even if the public facilitator is down.

πŸ† Tracks & sponsor tech β†’ where in the code

For judges β€” exactly which Pharos / sponsor tech PayGuard uses and where to find it.

Track / Tech How PayGuard uses it Where in the code
Pharos x402 (flagship rail) guarded client Β· resource server Β· in-process facilitator settling EIP-3009 src/guardrail.ts Β· src/server.ts Β· src/facilitator.ts Β· src/demo.ts
Phase 1 β€” Skill Anthropic Agent Skill module (the deliverable) SKILL.md
CertiK Skill Scanner (security standard) minimal SafeERC20 + ReentrancyGuard + strict-CEI escrow Β· 100% test coverage Β· Slither-clean contracts/AgentVault.sol
Pharos Atlantic (688689) real, verifiable on-chain settlement tx 0xfd6e66…

βœ… Proven (reproducible)

$ npm run bench          # GuardianRail β€” offline, deterministic
  βœ… 8/8 vectors handled correctly β€” 100% of unauthorized agent spends blocked.

$ forge test             # AgentVault + MockUSDC β€” OZ v5.6.1 / solc 0.8.28
  [PASS] testFuzz_BalanceMatchesTotalLocked(uint256) (runs: 256)
  [PASS] test_ReentrancyGuardBlocksReentry()        … (+19 more)
  21 tests passed; 0 failed; 0 skipped

$ forge coverage         # contracts/ (AgentVault + MockUSDC)
  100% lines Β· 100% statements Β· 100% branches Β· 100% functions

$ npm run server & npm run demo    # full guarded payment, end-to-end on Atlantic
  βœ… paid + received: { secret: 'PayGuard: safe x402 payment settled on Pharos Atlantic.' }
  # receiver USDC balance 2000 β†’ 3000 on-chain β€” a real settlement, not just a 200

The complete flow is verified end-to-end on Atlantic: a GuardianRail-guarded agent pays the x402 server, the in-process facilitator verifies and settles 0.001 USDC on-chain, and the agent receives the protected content β€” all on @x402/* v2.14.

πŸ§ͺ Engineering harness

Layer Tool Status
Type safety tsc --noEmit βœ…
Skill tests GuardianRail attack bench (8 vectors) βœ… 8/8
Contract tests Foundry β€” 256-run fuzz + reentrancy + EIP-3009 βœ… 21/21
Contract coverage forge coverage (contracts/) βœ… 100%
Static analysis (Solidity) Slither βœ… 0 high / 0 medium
Static analysis (TypeScript) CodeQL βœ…
On-chain E2E guarded payment settles on Atlantic βœ…
Secret scanning TruffleHog (--only-verified) βœ…
Supply chain npm audit + Dependabot βœ…

A 3-stage GitHub Actions pipeline (Quality Β· Security Β· Gate) runs on every push/PR β€” see .github/workflows/ci.yml. Locally: npm run ci Β· npm run typecheck Β· make security-scan.

πŸ” Security β†’ CertiK mapping

Control Attack stopped
targetAsset strict-equality token-spoofing / look-alike phishing
maxSpendPerCall + dailyBudgetRemaining wallet draining via looping / hallucinating agents
allowedPayees allowlist exfiltration to a rogue payee
eth_call pre-flight simulation paying into reverts / paused / blacklisted contracts
SafeERC20 + ReentrancyGuard + CEI reentrancy / non-standard-token drains
minimal contract (no streaming / upgrade / delegatecall) reduced scanner attack surface

πŸ”— Phase 1 on-chain proof (Pharos Atlantic, 688689)

Artifact Value
MockUSDC (EIP-3009) 0xe54205649D6d41Aa9cCdD5667eaDB62f1dFA84AC
Settlement tx 0xfd6e66157765066d9ff76068ee9476549153ade951036f3f7863a29f2ffbc253

Verified on-chain β€” status: success, block 24099194. Check it yourself: eth_getTransactionByHash on RPC https://atlantic.dplabs-internal.com/.

πŸ“ Repo layout

SKILL.md                     Anthropic Agent Skill (the submission artifact)
src/guardrail.ts             Tool 1 β€” createGuardedFetch + pure evaluateRequirement
src/simulate.ts              eth_call pre-flight (injectable)
src/rpc.ts                   rate-limit-hardened viem transport (retryingHttp)
src/facilitator.ts           in-process x402Facilitator (self-hosted fallback)
src/server.ts                x402-protected resource server
src/demo.ts                  end-to-end guarded payment
src/agent.ts                 Skill-to-Agent β€” autonomous Claude agent on the guard
src/probe.ts                 EIP-3009 settlement probe
contracts/AgentVault.sol     Tool 2 β€” conditional escrow (100% coverage)
contracts/MockUSDC.sol       EIP-3009 test USDC (100% coverage)
script/DeployMockUSDC.s.sol  deploy MockUSDC β†’ USDC_ADDRESS
test/AgentVault.t.sol        AgentVault suite (fuzz + reentrancy)
test/MockUSDC.t.sol          EIP-3009 transferWithAuthorization suite
bench/malicious_bench.ts     8/8 attack-vector bench

Stack

@x402/* v2.14 Β· viem v2.52 Β· express Β· Foundry (solc 0.8.28, OZ v5.6.1) Β· TypeScript/ESM. MIT Β© 2026 Edy Cu.

About

πŸ’‚ The CertiK-grade seatbelt for x402 β€” safe, guarded agent payments on Pharos.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors