DocsOverview
AI trading agents are a hot narrative · but handing an AI your private key is reckless: prompt injection, hallucination, or a bug can drain everything. Most "AI agent wallets" today rely on off-chain promises.
Emet's answer: a smart account (the Clay) where the AI holds only a session key (the Mark). Every action is checked onchain against rules the user sets (the Inscription) · allowed assets, max position size, max daily loss, trade caps, allowed venues, max slippage, and expiry. The Maker keeps the master key. The AI can be anything · Claude, GPT, a custom model, a copy-trading bot · because the guardrails never depend on trusting it.
Brand & voice
The product is about control, not hype. Tone: calm, precise, confident.
Sensitivity
Emet is a meaningful word in Jewish tradition, and the Golem legend is widely used in culture · the name is fine, but treat it with respect. Use it as the myth of a bounded servant. Never pair Hebrew letters with joke copy, and never use divine names.
Lore glossary
Use the Emet name in the UI, docs, and marketing. Use the code name in contracts and the SDK · a new developer should be able to read the code without learning the mythology.
| Concept | Emet name | Code name |
|---|---|---|
| User / master key | Maker | maker |
| AI agent | Golem | agent |
| Smart account | Clay | EmetAccount |
| Policy module | Inscription | InscriptionModule |
| Individual rules | Letters | Rule |
| Session key | Mark | sessionKey |
| Kill switch | Erase | erase() |
| Active state | Emet | State.EMET |
| Frozen state | Met | State.MET |
| Guardian | Keeper | keeper |
| Audit trail | Chronicle | Chronicle* events |
| Blocked breach attempts | Refusals | Refused event |
| Agent marketplace (v2) | The Workshop | registry |
Architecture
Checks are split by execution phase, because ERC-4337 validation limits which external storage can be read · a Chainlink read inside validateUserOp would get the bundle rejected.
Golem · signs UserOp with Mark
VALIDATION (validateUserOp) · Mark valid & unexpired? · account state == EMET? · target a whitelisted adapter? · selector known? default-deny
EXECUTION · Adapter → Uniswap / opening auction
POST-EXECUTION HOOK (ERC-7579) · recipient == EmetAccount? · fill ≥ oracle quote × (1 − maxSlippage)? · position limits still ok? · NAV ≥ high-water mark × (1 − maxDrawdown)? · oracle feeds fresh?
pass → Chronicle event fail → revert
Key design decisions
Cheap, storage-local checks go in validation. Oracle-based checks (NAV, drawdown, slippage) go in an ERC-7579 post-execution hook that reverts on breach.
We never decode Uniswap's Universal Router · its command-byte format is a large bypass surface. A small SwapAdapter exposes only swapExactIn(tokenIn, tokenOut, amountIn, minOut) and always sends output to the calling account.
Any unknown target or selector is rejected · including the account's own functions. The Mark can never call execute for transfers, module changes, or approvals to arbitrary spenders.
Every trade's output recipient must be the EmetAccount itself. This is what makes "no withdrawals" hold.
Require minOut ≥ oracleQuote × (1 − maxSlippage). Without this, an agent can bleed value inside every other limit through bad fills or sandwich attacks.
If any held asset's feed is older than maxOracleAge, trading is blocked. The account is never valued on stale prices.
Contracts reference
The moving parts of the protocol layer, named in both worlds.
The Clay. Holds funds, validates every UserOp against the account's State, and delegates policy checks to the InscriptionModule. Never exposes a direct transfer path to the Mark.
Holds the Letters (Rule set) for an EmetAccount. Runs pre-execution checks during validation and post-execution checks via the ERC-7579 hook.
One enforceable constraint: asset allowlist, max position size, max daily loss, trade size/frequency cap, venue whitelist, max slippage, or expiry.
The only whitelisted execution target for trading. A thin wrapper around a DEX (Uniswap for MVP) that always returns output to the calling EmetAccount · never to an arbitrary address.
The account's two states. EMET: Golem can trade within its Letters. MET: frozen · all Marks revoked, no execution possible until the Maker re-inscribes.
Moves the account to State.MET instantly and revokes every Mark. Auto-triggered after N consecutive Refused events (configurable) or a hard drawdown breach. Only the Maker can return the account to EMET.
The Golem's signing key. Scoped to trade-only actions, dies after N days, and is revoked instantly on erase().
Every successful action emits a Chronicle* event, feeding the public performance page. Reverted UserOps emit nothing onchain · the bundler/SDK layer simulates each one and logs Refused off-chain, tagged with the rule that failed.
Base Sepolia Testnet Deployments (Chain ID 84532)
| Contract | Address | Explorer |
|---|---|---|
| InscriptionModule | 0x7E758484ECd3321B628Ba021017FdfB95e22c208 | Basescan |
| EmetFactory | 0xC875a21d24d8c3C8cB774EF9645FE66B5E6ee8F3 | Basescan |
| SwapAdapter | 0xb269c0F4E1ACeB606DC5Cfc9826db256D5111a55 | Basescan |
| MockDexRouter | 0xcF09199bf919B99c1eAf60E441688ffbD335A4f6 | Basescan |
| Clay (Demo Account) | 0x5b46e423fb13d1f3ba69fc685ba9b7633f7f4089 | Basescan |
| USDG (Faucet Token) | 0x5659Eb1eF33d4B94D74594b60391f7FA94196a90 | Basescan |
| NVDA (Stock Token) | 0xf3DC1C78044d3D286F3654bf18801936Dc66D4A1 | Basescan |
| TSLA (Stock Token) | 0x18CD5a3a248f21b16872F4CD47F3252673b4B82e | Basescan |
The Letters
Rules live onchain, enforced across two phases: cheap checks during validateUserOp, oracle-dependent checks in the ERC-7579 post-execution hook.
| Letter | Enforces | Phase | Example |
|---|---|---|---|
| Allowed assets | Only listed tokens may be traded | Validation | NVDA, TSLA, USDG |
| Max position size | Cap on any single asset's share of NAV | Post-hook | ≤ 20% of portfolio |
| Max daily loss | Drawdown vs. high-water mark over a 24h UTC window | Post-hook | −5% → auto-freeze |
| Trade size & frequency | Per-trade size and a per-hour trade count cap | Validation | ≤ 6 trades / hour |
| Allowed venues | Only whitelisted adapters as call targets | Validation | SwapAdapter (Uniswap) |
| Max slippage | Fill must clear the oracle-anchored bound | Post-hook | fill ≥ oracle × (1 − 0.5%) |
| Expiry | The Mark dies after N days · no silent renewal | Validation | 30 days |
| No withdrawals | Output recipient must always be the EmetAccount | Post-hook | enforced, not optional |
Agent SDK
@emet/sdk · a TypeScript SDK that builds, simulates, and submits UserOps, and mirrors Refusals back with the exact rule that would block them.
import { EmetClient } from "@emet/sdk"; const emet = new EmetClient({ clay: "0x5b4...089", mark: process.env.SESSION_KEY, chain: "base-sepolia", }); const plan = await emet.simulate({ action: "swap", tokenIn: "USDG", tokenOut: "NVDA", amountIn: "250", }); if (plan.refused) { console.log(`Refused by ${plan.rule}`); } else { await emet.submit(plan); } // listen for the Chronicle in real time emet.on("chronicle", (evt) => console.log(evt)); emet.on("refused", (evt) => console.log(evt.rule));
# install & point at a chain npx @emet/sdk init --chain base-sepolia # write the Inscription emet inscribe --clay 0x5b4...089 \ --assets NVDA,TSLA,USDG \ --max-position 20% \ --max-daily-loss 5% \ --expiry 30d # mint a Mark for your Golem emet mark:create --clay 0x5b4...089 --ttl 30d
Method reference
| Method | Description |
|---|---|
simulate(action) | Simulates a UserOp against the live Inscription; returns a plan or a refused flag with the failing rule. |
submit(plan) | Builds, signs with the Mark, and submits the UserOp through the bundler. |
on("chronicle", fn) | Subscribes to successful, onchain-confirmed actions. |
on("refused", fn) | Subscribes to blocked attempts, tagged with the Letter that stopped them. |
erase() | Maker/Keeper-only. Freezes the Clay immediately. |
Tech stack
| Contracts | Solidity, ERC-4337 + ERC-7579 modules on the Safe or Kernel account stack; Foundry for tests |
| Infra | Bundler and paymaster on Base Sepolia Testnet (Chain ID 84532) |
| Oracles | Chainlink feeds for valuation and slippage checks |
| SDK | TypeScript agent SDK (@emet/sdk) that builds, simulates, and submits UserOps |
| Demo Golem | An LLM plus a simple strategy (rebalancer or momentum) |
Milestones
| # | Deliverable | Est. |
|---|---|---|
| M1 | InscriptionModule + SwapAdapter + unit/fuzz tests (breach attempts) | 3 wk |
| M2 | 4337 integration (bundler, paymaster), Marks (session keys) | 2 wk |
| M3 | Agent SDK + demo Golem + Refusal logging | 2 wk |
| M4 | Website, dashboard + testnet launch | 2–3 wk |
M1 test requirements
Fuzz every Letter with breach attempts, including: wrong recipient, unknown selector, direct token transfer/approve, expired Mark, stale oracle, slippage beyond bound, trades after erase(), and a drawdown crossed mid-window.
Risks
Bypass surface
Any decodable path left open is a potential drain. Default-deny and adapter-only targets keep this small.
Oracle risk
Loss checks inherit oracle risk; the stale-oracle policy mitigates it.
Regulatory
If Emet provides the strategy, that may count as investment advice or management. Being the infrastructure is safer · the demo Golem is clearly labeled as a demo.
Success metrics
Refusals · breach attempts blocked onchain · double as a marketing stat: proof the guardrails hold, not a promise that they do.