Trustless allowance for AI agents: cap it, scope it, kill it. Built on 0G.
DoNotRugMe is a permission layer that lets a user delegate scoped, revocable spending authority to an AI agent — enforced on-chain, not by trust. The name says the guarantee: the agent literally cannot rug you.
THE PROBLEM AI agents are increasingly asked to move money — pay for compute, rebalance, buy inference, settle invoices. Today you have two bad options: hand the agent a raw private key (it can drain everything, forever), or keep a human in the loop on every action (defeats the point of an agent). There is no native way to say "you may spend up to X of token Y per day, only to these targets, until Friday, and I can cut you off instantly."
WHAT WE BUILT The user controls a passkey smart account (ERC-4337, EntryPoint v0.7, WebAuthn P-256 keys — no seed phrase, unlock with Face ID / Touch ID). Instead of sharing keys, they sign a permission grant that defines: • a token whitelist + allowed function selectors per target • a per-period spend allowance (rolling window, tracked on-chain) • a validity window [start, end] • an owner-only kill switch (block / unblock any time) • optionally: a per-call TEE attestation requirement
The full permission record is stored on 0G Storage; its locator (merkle root) is pinned on-chain via the AgentPermissionManager. The agent holds its OWN key (a plain EOA) — it never touches the user's account.
HOW A TRANSACTION FLOWS At runtime the agent: (1) finds its grant from PermissionApproved logs, (2) resolves the 0G Storage root via getLocator, (3) downloads and merkle-verifies the record, (4) checks getHash(record) == on-chain hash && isApproved, then (5) calls executeBatch on the manager. The manager RE-CHECKS every rule per call and reverts on violation:
• over allowance → ExceededSpendLimit(requested, remaining) • non-whitelisted fn → UnauthorizedCall(target, selector) • agent blocked → AgentBlocked(agentId) • outside window → grant inactive
The kill switch is a single owner tx, effective immediately — the agent can hammer, every attempt reverts until unblock. Nothing is trusted client-side; the contract is the enforcer.
ASK-FOR-APPROVAL Over-cap calls aren't silently dropped. The agent files them to a relay as approval requests the owner reviews in a dashboard and signs off with their passkey — the relay holds NO authority, the owner's account executes. So the agent runs autonomously inside its limits and only escalates to a human when it wants more than it was granted.
VERIFIABLE AGENT IDENTITY AgenticID is a standalone ERC-721 identity/pubkey registry: tokenId == uint256(delegateAddress), and agentIdOf(tokenId) is exactly the value passed as Permission.agentId. This ties every grant to a portable, on-chain agent identity.
TEE-GATED ACTIONS A call can require a TEE attestation: an 0G Compute model signs a response bound to the exact calldata digest; TeeAttestationChecker verifies the 0G TeeML signature on-chain before the transfer is permitted. Proven end-to-end live with a real qwen2.5-omni provider.
CONTRACTS AgentPermissionManager enforces token whitelist, per-period spend caps, validity window and kill switch. Permission hashing uses a pinned PERMISSION_TYPEHASH (0xf19781d0…0660), verified live on-chain against source. TeeAttestationChecker.submitTeeProofResponseBound verifies 0G Compute's native TeeML signature format (reqSha:respSha:type:identity:tlsFp), binding the proof to the exact calldata digest so a signed model response can't be replayed on a different call.
ACCOUNT + BUNDLER Passkey smart account (JustanAccount) on the canonical ERC-4337 EntryPoint v0.7. UserOps go through a real Skandha bundler (v2.4.5). A single passkey ceremony batches account deploy + addOwner + approve + setLocator into ONE userop, so onboarding an agent is one Face ID prompt.
0G STORAGE Permission records live on 0G Storage. The 0G Storage SDK is Node-only, so the browser reads/writes through a small Express relay; the agent daemon and SDK read directly and merkle-verify. Duplicate upload ("already exists") is treated as success.
0G COMPUTE (TEE) Live mode funds a broker ledger (>=3 0G), acknowledges the provider signer, runs live qwen2.5-omni inference, and the provider's TEE signature is verified on-chain. Mock mode uses a local signer for fast demos.
HACKY BITS WORTH FLAGGING (all bitten, all encoded in code) • 0G has no RIP-7212, so stock solady P256 fails SILENTLY — we vendored a patched verifier pointing at 0x0A0BE84e…21DEc so passkey sigs actually verify on-chain. • Skandha's gas floor MOVES (seen 2→4 gwei) and silently DROPS underpaying userops (accepts the hash, never bundles). We read skandha_getGasPrice + a margin before submitting. • Never re-send a pre-signed userop via viem sendUserOperation — it re-signs, changes the hash, and you end up polling a hash that was never submitted. We push the exact signed bytes over raw eth_sendUserOperation. • WebAuthn userops need verificationGasLimit >= 800k or they revert. • Canonical EntryPoint v0.8 has NO code on 0G — only v0.7 is served; tooling that defaults to v0.8 fails mysteriously. • 0G receipts lag; poll getTransactionReceipt (90×2s) and check nonce before any retry, or you double-spend the nonce. • Open the wallet as http://localhost (not 127.0.0.1) — an IP can't be a WebAuthn rpId.
LIVE DEPLOYMENTS — 0G Galileo (chain 16602) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ AgentPermissionManager 0xcf7cc04a8aeae2da89b9ea19ca64241f29c33d15 TeeAttestationChecker 0x6522c6dEc5A837E764F2cb0dDD12ba859721dF6e JustanAccountFactory 0x843deca25193c5f493fd45fc5642fd57999bfc22 EntryPoint v0.7 (canon.) 0x0000000071727De22E5E9d8BAf0edAc6f37da032 AgenticID (ERC-721) 0xdf0Ba5Bdabda1F06F2d31404b7D571A29984117D dUSD (ERC20Mock) 0x6d3c795c01c55c24699e1823d8b7edff85a87c9b Bundler (Skandha v2.4.5) https://bundler.valanamal.xyz/rpc

