marmot

Marmot docs

20 pages, from the idea to the error codes

Start

Quickstart

Try it in the browser, then from the terminal, then from your own code.

1. In the browser (30 seconds)

Open the demo on the home page. You hold a stolen key. Press "Send 4 ETH to me", skip time with "+1 day", try "Try to execute now", then let the guardian whistle. Tick "The guardian is asleep" to see what an unattended vault costs you.

2. Run the site locally

launch.bat          # builds and serves on http://localhost:5900

3. Run the proofs

cd contracts && forge test                  # 43 tests
node --test tests-js/engine.test.mjs         # engine vs the real contract on a local node, real P-256
node --test tests-js/diff.test.mjs           # 560 random steps, JS simulator vs contract
node scripts/real-chain-check.mjs            # real chain 4663, real P-256 precompile
node scripts/anvil-e2e.mjs                   # full CLI + watcher run on a local node

4. The CLI

Keys are read from the environment, never from arguments.

set MARMOT_PRIVATE_KEY=0x...
set RPC=https://rpc.mainnet.chain.robinhood.com

node cli/marmot.mjs deploy                                   # once per network, prints the factory
set MARMOT_FACTORY=0x...
node cli/marmot.mjs create --guardian 0xGuardianWallet --delay 24h
set MARMOT_VAULT=0x...

# send some ETH to the vault address with any wallet, then:
node cli/marmot.mjs queue-eth %MARMOT_VAULT% --to 0xFriend --amount 0.5
node cli/marmot.mjs status  %MARMOT_VAULT%
node cli/marmot.mjs execute %MARMOT_VAULT% 0                 # once it is ripe
Before real money

The contract is not audited. Start with an amount you can afford to lose, a short waiting time, and read Limits and risks.

5. A passkey guardian

On the Guardian page your browser makes a passkey and shows its public coordinates. The CLI takes them:

node cli/marmot.mjs create --guardian-passkey 0x<x>,0x<y> --rp your.domain --delay 24h

Then open the Lookout on the vault address.

6. The terminal lookout

node cli/marmot.mjs watch %MARMOT_VAULT% --notify-url https://discord.com/api/webhooks/<your-private-webhook>

See Lookout and watcher for auto-refusing anything that is not on an allow list.

7. From your own code

import * as E from './src/engine/marmot-engine.mjs';
import * as P from './src/engine/passkey.mjs';

const auth = await P.softAuthenticator({ rpId: 'marmot.example', origin: 'https://marmot.example' });
const guardian = E.guardianPasskey(auth.x, auth.y);
const vault = E.predictVault(FACTORY, { owner, guardian, delay: 86400, rpIdHash: auth.rpIdHash, salt });

// later, to refuse announcement 3:
const digest = E.vetoDigest(4663, vault, 3, /* epoch */ 0);
const sig = await P.signWithPasskey(auth, digest);
await relayer.sendTransaction({ to: vault, data: E.call.vetoWithSig(3n, sig), gasLimit: 900000n });