Skip to content

Contracts

Foundry project in contracts/. Solidity 0.8.24, Solady for WebAuthn, P256 and signature checking. All addresses and transaction hashes below are also in contracts/deployments/monad-testnet.json.

Monad testnet deployment

Chain id 10143. Deployer 0x61c780065C2F803588201F4469b2F80484da448f. Private submitter in BTX mode.

Contract Address
MandateRegistry 0x46441BC77a4dDbaE7004943E0ab9cB01c76092fA
RiskBreaker 0xf4c2F2373a17e3a2122f984D512B0B2EabA26374
MandateExecutor 0xbb2d989876BFdf63CDFf7bb480A667175cF12409
PrivateSubmitter 0xC552018AA7A9001e1dEcdfe40dAe38Dd6C5ca9D9
ERC8004ReputationAdapter 0x3b1d977C1270dF25252041D0671b6FD90dF7a757

External, verified on-chain:

Dependency Address
P256 precompile (RIP-7212) 0x0000000000000000000000000000000000000100
ERC-8004 Identity Registry 0x8004A818BFB912233c491871b3d84c89A494BD9e
ERC-8004 Reputation Registry 0x8004B663056A597Dffe9eCcC1965A193B7388713
Demo agent (ERC-8004 id) 1831

Roles

  • Principal: a human. Controls a PasskeyAccount. Issues and revokes mandates.
  • Agent: an ERC-8004 identity with its own executing key.
  • Venue: any contract the mandate whitelists.
  • Attestor: the only address allowed to write reputation through the adapter.

The Mandate struct

struct Mandate {
    address principal;      // PasskeyAccount (or any ERC-1271 account / EOA)
    uint256 agentId;        // ERC-8004 id
    address agentKey;       // executing key
    address[] targets;      // allowed contracts
    bytes4[]  selectors;    // allowed selector per target (parallel array)
    address   asset;        // spend asset, address(0) = native
    uint256   spendCap;     // lifetime cap
    uint256   perBlockCap;  // per-block cap
    uint256   maxDrawdownBps;
    uint64    validAfter;
    uint64    validUntil;
    uint256   nonce;        // per principal
    bytes32   policyHash;   // commitment to the encrypted off-chain policy blob
}

EIP-712 domain: name = "Mandate", version = "1", chainId, verifyingContract = MandateRegistry.

Contract by contract

PasskeyAccount. Minimal smart account whose owner is a P256 public key. isValidSignature verifies an ABI-encoded WebAuthn assertion: client data type and challenge, authenticator flags (user present and user verified), low-s, then the signature through the precompile via Solady's audited library. Owner actions (execute, revokeMandate) are authorised by an assertion over a nonce-bound digest. The trusted executor has a dedicated hook, and venue reverts bubble up unchanged.

MandateRegistry. grant verifies the principal's signature (ERC-1271 for accounts, ECDSA for EOAs), enforces the nonce and input sanity, stores the mandate and arms the breaker. revoke is principal-only and immediate. validate is a view that reverts with TargetNotAllowed, SpendCapExceeded, PerBlockCapExceeded, MandateExpired, MandateNotYetValid, MandateRevoked or Tripped. recordExecution is executor-only and re-checks the caps.

MandateExecutor. execute(mandateHash, target, data, amount) from the agent key: validate, call from the principal's account, measure real outflow, require it does not exceed amount, record spend, checkpoint the breaker, emit MandateExecuted. executeFor is the same path for the private submitter.

RiskBreaker. Per-mandate hysteresis state machine. Armed to Tripped when drawdown exceeds maxDrawdownBps. Tripped to Cooldown after cooldownBlocks. Cooldown to Armed when drawdown falls under maxDrawdownBps × rearmFactorBps / 10000. checkpoint is permissionless. Emits Tripped(mandateHash, agentId, drawdownBps, block).

PrivateSubmitter. One contract, two modes chosen at deploy. BTX mode passes through to the executor because privacy comes from the encrypted mempool. Commit-reveal mode requires commit(hash) from the same key at least one block before reveal(calldata) and within a reveal window.

ERC8004ReputationAdapter. attest(agentId, {complianceScore, tripCount, executedCount, realisedPnlBps, windowStart, windowEnd, evidenceHash}) from the attestor only. Stores the attestation, mirrors it to the ERC-8004 Reputation Registry with giveFeedback, and emits ReputationAttested. A failing mirror emits MirrorFailed rather than losing the record.

Events the indexer follows

Contract Event
MandateRegistry MandateGranted, Revoked, ExecutionRecorded
MandateExecutor MandateExecuted
RiskBreaker Armed, Tripped, CooldownEntered, Rearmed, PeakReset
ERC8004ReputationAdapter ReputationAttested

Build and test

cd contracts
forge test                                  # 67 tests: unit + invariants at 256 runs
forge script script/Deploy.s.sol --rpc-url $MONAD_RPC_URL --account <keystore> --broadcast
forge script script/E2E.s.sol    --rpc-url $MONAD_RPC_URL --account <keystore> --broadcast

The tests run under the Osaka EVM so the P256 precompile is present locally and passkey signatures are verified for real.