Skip to content

jstinhw/mallow

Repository files navigation

mallow

The agent-native wallet. A agent native wallet where a guardian agent reads every transaction and signature before it's signed, and where third-party agents are installed, validated, and managed like apps — never trusted by default.

Why an agent-native wallet

Agents are starting to move money. A yield bot wants to rebalance your stables, a trading agent wants a session key, a protocol wants you to sign an approval. The old wallet model — "click sign, hope for the best" — doesn't survive contact with software that acts on your behalf. mallow is built around two ideas:

1. Every tx and signature passes through a guardian agent

Nothing gets signed blind. Whether the request comes from a dApp over WalletConnect, from you, or from an installed agent, the Guardian reviews it first and returns a plain-language verdict with a risk level (infolowmediumhighcritical). High/critical verdicts require typed confirmation before the wallet will sign.

The Guardian isn't a regex over calldata — it's a small multi-agent pipeline:

  • Planner — picks the minimum set of analysis tools to run for this request.
  • Analyzer — investigates by calling tools over several rounds:
    • decode_calldata — decode the function and arguments
    • get_contract_info — verification status, ABI, proxy/implementation (Etherscan)
    • lookup_sourcify_signature — resolve unknown selectors via Sourcify
    • simulate_transaction — execute against an anvil fork of live state to capture balance changes, ERC-20/721 transfers, storage diffs, and reverts
  • Summarizer — turns the evidence into a verdict a non-developer can act on: what happens, what it costs, and whether it's safe.

The result is a GuardianVerdict — risk level, summary, concrete asset impacts, findings, the decoded call, and the simulation diff — streamed to the UI as it works.

2. The wallet validates, installs, and manages agents — with no trust

Third-party agents talk to mallow over a small agent protocol (@mallow/agents-protocol). An agent service exposes its intent as an AgentRequest (a list of tx and sig actions); the wallet fetches it, runs it through the Guardian like any other request, shows you exactly what will be sent or signed, and only then executes.

Crucially, the wallet signs or broadcasts but never interprets what a signature authorizes. An agent can ask for, say, a ZeroDev session-account approval, and mallow signs the bytes verbatim — the agent's implementation never leaks into the wallet, and the wallet never has to trust the agent's claims about what it's doing. The Guardian is what closes that gap: it independently validates the request instead of taking the agent's word for it.

Install / manage flow per agent: /request (what it wants) → Guardian review → /install (execute + register) → /status (what it's doing) → /uninstall (revoke and unwind positions). Installations are stored per-wallet; the Guardian itself is an always-on builtin.

Two reference agents ship in the repo:

  • Guardian (guardian-agent-service) — the always-on transaction reviewer above.
  • Sprout / auto-invest (auto-invest-agent-service) — moves idle USDC into vetted yield venues and pulls it back when rates dip. A worked example of a third-party agent driving the wallet through the protocol.

Monorepo layout

A pnpm workspace with three top-level services:

  • wallet/@mallow/wallet, the Next.js wallet app (passkey-encrypted EOA, WalletConnect, approval queue, agent management UI).
  • agents-protocol/@mallow/agents-protocol, the shared agent-protocol wire types.
  • agents/ — runnable agent services:
    • agents/guardian-agent-service@mallow/guardian-agent-service
    • agents/auto-invest-agent-service@mallow/auto-invest-agent-service

The repo root holds only workspace config and delegating scripts. Run a service via its prefixed script (pnpm wallet:<cmd>, pnpm agent:guardian:<cmd>, pnpm agent:auto-invest:<cmd>), or target any package directly with pnpm --filter <pkg> <cmd>.

How signing works (request lifecycle)

dApp (WalletConnect) ─┐
you (send / sign)    ─┼─▶  Guardian  ─▶  verdict (risk + simulation + summary)
installed agent      ─┘   plan→analyze→summarize        │
                                                        ▼
                              high/critical → typed confirmation required
                                                        │
                                                        ▼
                                   passkey unlock → sign / broadcast

The EOA private key is encrypted at rest with AES-GCM under a key derived (HKDF-SHA-256) from your passkey's PRF output — so unlocking to sign is a passkey prompt, not a seed phrase.

Prereqs

  • pnpm, Node ≥ 20
  • Foundry (cast, anvil) — curl -L https://foundry.paradigm.xyz | bash && foundryup (the Guardian forks with anvil to simulate)
  • LM Studio (or any OpenAI-compatible endpoint) running locally with a chat-completions model loaded — the Guardian's planner/analyzer/summarizer run on it

Setup

# env is per-service; see .env.example for which keys go where
cp .env.example wallet/.env.local   # edit: full wallet env
cp .env.example .env.local          # edit: trim to the agent subset
pnpm install
pnpm wallet:test
pnpm wallet:dev          # wallet on :3000
pnpm agent:guardian:dev  # Guardian service on :3002

To run the agent services together with their Postgres, use docker-compose up.

Env

Env is per-service: the wallet reads wallet/.env.local; agent services read the root .env.local. Shared keys (RPC, ZeroDev, Morpho vault) are duplicated across both. See .env.example for the per-service grouping.

  • ETHERSCAN_API_KEY — required (etherscan.io, used via the v2 multichain endpoint for contract verification/ABI lookups)
  • ALCHEMY_API_KEY — required for token portfolio discovery via the Alchemy Portfolio API
  • LLM_BASE_URL — default http://localhost:1234/v1
  • LLM_MODEL — default qwen/qwen2.5-coder-14b-instruct
  • LLM_API_KEY — any non-empty placeholder
  • NEXT_PUBLIC_REOWN_PROJECT_ID — WalletConnect / Reown project id (free, cloud.reown.com)
  • NEXT_PUBLIC_GUARDIAN_AGENT_URL — where the wallet reaches the Guardian (default http://localhost:3002)
  • NEXT_PUBLIC_AUTO_INVEST_AGENT_URL — where the wallet reaches Sprout (default http://localhost:8787)
  • ZERODEV_URL, DATABASE_URL — agent-service only (session accounts, auto-invest state)

Smoke checklist

  1. pnpm wallet:test — unit + integration.
  2. Set ETHERSCAN_API_KEY in wallet/.env.local; boot LM Studio with a model on :1234.
  3. pnpm agent:guardian:dev (Guardian on :3002) and pnpm wallet:dev.
  4. Walk through: create wallet → unlock → pick the "infinite approve unverified" demo → run Guardian → confirm verdict shows high or critical and requires typed confirmation. Cancel.
  5. Pick "send 0.001 ETH" demo → run Guardian → expect low/info. Sign and broadcast only from a wallet funded.
  6. Install the Sprout agent → confirm its AgentRequest is reviewed by the Guardian before any signature is produced.
  7. Confirm the tx on Basescan.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages