The TubbiPlanbook contract
A small contract with no owner, no fee, no pause and no proxy. It keeps plans, not money: ETH passes through in the same call, tokens go straight from the plan owner to the recipient.
Address
0x1C298E9B9e2708779Eb09a49A8560DfAf6aCCf8D on Robinhood Chain, deployed at block 94,925,094. Source verified: Blockscout.
Functions
openPlan(bytes32 planHash, uint40 deadline, uint128 ethCap, bytes32[] stepHashes) returns (uint256 id)
runEth(uint256 id, uint8 index, address to) payable
runToken(uint256 id, uint8 index, address token, address to, uint256 amount)
runNote(uint256 id, uint8 index, bytes32 memo)
closePlan(uint256 id)
getPlan(uint256 id) view · stepHashOf(id, index) view · isDone(id, index) view
hashStep(uint8 kind, address token, address to, uint256 amount, bytes32 memo) pureA step hash is keccak256(abi.encode(kind, token, to, amount, memo)) with kind 1 for ETH, 2 for a token, 3 for a note. The workspace computes the same hashes in the browser, and a test checks them against values produced by the compiled contract.
Limits and checks
- 1 to 16 steps per plan; no empty step hash.
- Deadline in the future and at most 30 days away; a step after it reverts with PlanExpired.
- Only the wallet that opened a plan can run or close it (NotOwner).
- A step must match its committed hash (StepMismatch) and runs once (StepDone).
- ETH steps together never exceed the cap (OverEthCap).
- Token steps refuse your own address as recipient, refuse non-contract tokens, and revert if the recipient's balance does not rise by exactly the amount (TokenAmountChanged), which rules out fee-taking tokens.
- A reentrancy lock covers both money paths; the contract has no receive function.
Events
PlanOpened(uint256 indexed id, address indexed owner, bytes32 planHash, uint40 deadline, uint128 ethCap, uint8 stepCount)
StepRun(uint256 indexed id, uint8 indexed index, uint8 kind, address token, address to, uint256 amount, bytes32 memo)
PlanClosed(uint256 indexed id, uint16 doneMask)Tests
Unit tests for every function and error, fuzz tests on the cap, deadline, amounts and recipients, invariant tests (the contract never holds ETH or tokens; recipients received exactly what successful steps sent; per-plan spend never above the cap), fork tests against the real USDG and WETH contracts on Robinhood Chain, and an end-to-end run of the workspace against a local chain with an injected wallet.