Reference
Contract reference
Marmot.sol and MarmotFactory.sol, function by function.
Solidity 0.8.26, optimizer 500 runs, via-IR, EVM cancun. Source in contracts/src/. The vault inherits OpenZeppelin EIP712 and uses SignatureChecker and the repository's WebAuthn library.
Constants and state
| Name | Value | Notes |
MIN_DELAY | 5 minutes | |
MAX_DELAY | 30 days | |
GRACE | 14 days | How long a ripe announcement stays runnable |
owner | address | Changes only by an announcement |
guardian | (address addr, uint256 x, uint256 y) | addr != 0: wallet or ERC-1271. Otherwise a P-256 passkey (x, y). |
delay | uint64 | |
rpIdHash | bytes32, immutable | sha256 of the passkey domain |
epoch | uint64 | Incremented by panic and by owner rotation |
opCount | uint256 | Next announcement id |
Owner functions
| Function | Effect |
queueCall(to, value, data) returns (id) | to may not be the vault or zero |
queueTransfer(token, to, amount) returns (id) | ETH if token == 0. to may not be zero or the vault. |
queueSetDelay(newDelay) returns (id) | Within MIN_DELAY and MAX_DELAY |
queueSetGuardian(g) returns (id) | Valid per the creation rules |
queueSetOwner(o) returns (id) | o != 0 |
cancel(id) | Pending announcements only |
Public functions
| Function | Effect |
execute(id) | Runs a pending, ripe, unexpired announcement of the current epoch. Reentrancy-locked. |
vetoWithSig(id, sig) | Verifies the guardian's signature on Veto(id, epoch), then refuses the announcement |
panicWithSig(sig) | Verifies the guardian's signature on Panic(epoch), then epoch + 1 |
receive() | Accepts ETH, emits Deposited |
Guardian functions (wallet guardian only)
| Function | Effect |
veto(id) | msg.sender must be guardian.addr |
panic() | Same, epoch + 1 |
Views
| Function | Returns |
getOp(id) | (kind, status, readyAt, epoch, to, value, data) |
isLive(id) | Pending, current epoch, not expired |
vetoDigest(id), panicDigest() | The EIP-712 digest to sign, for the current epoch |
EIP-712 messages
domain: { name: "Marmot", version: "1", chainId, verifyingContract: vault }
Veto(uint256 id, uint64 epoch)
Panic(uint64 epoch)
For a passkey guardian the 32-byte digest is the WebAuthn challenge. For a wallet or ERC-1271 guardian the signature is the usual 65-byte (or contract-defined) signature over the digest.
Events
| Event | When |
Deposited(from, amount) | ETH received |
Queued(id, kind, to, value, data, readyAt, expiresAt) | Any announcement. Everything needed to explain it is here. |
Executed(id, by) | An announcement ran |
Vetoed(id, by) | The guardian refused it. by is the caller (a relayer for signed vetoes). |
Cancelled(id) | The owner withdrew it |
Panicked(newEpoch) | Panic, or owner rotation |
OwnerChanged, GuardianChanged, DelayChanged | At creation and when an announcement changes them |
MarmotFactory
create(owner, guardian, delay, rpIdHash, salt) returns (Marmot)
predict(owner, guardian, delay, rpIdHash, salt) returns (address)
event Created(vault, owner, delay)
The CREATE2 salt is keccak256(owner, salt), so nobody can squat an address meant for you. The factory has no owner, no fee and no authority over the vaults it creates.