Contract Analysis
DISCLAIMER // NFA // DYOR
This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.
⊙ generated by robots | curated by humans
Analysis Date: 2026-05-02
Metadata
Primary Contract
| PROPERTY | VALUE |
|---|---|
| Contract Address | 0x00000000...feAaBC (etherscan) |
| Network | Ethereum Mainnet |
| Contract Type | Standalone (deploys companion SLOWGate via CREATE2 in constructor) |
| Deployment Date | 2026-04-29 15:19:35 UTC |
| Deployment Block | 24986598 |
| Contract Creator | 0x1C0Aa8cC...855A20 (etherscan) (z0r0z.eth) |
| Creation TX | 0xb98494e3...3fa7bb (tx) |
| Compiler Version | Solidity 0.8.34 (viaIR, optimizer 200 runs, Prague EVM) |
| Total Functions | 33 declared on SLOW + 7 on SLOWGate (plus inherited Solady ERC1155 / Multicallable) |
| External Contract Dependencies | 2 internal (SSTORE2 HTML chunks) + 1 deployed-by-this-contract (SLOWGate); no external protocol calls except optional SafeTransferLib paths into ERC-20 token contracts supplied by users |
| Upgrade Mechanism | ☒ None — Not Upgradable |
| Verification Status | ☑ Verified on Etherscan (Exact Match) |
| Audit Status | ☒ No audit report observed |
Related Addresses
| TYPE | ADDRESS | NOTES |
|---|---|---|
| Deployer / Author | 0x1C0Aa8cC...855A20 (etherscan) |
EIP-7702 delegated EOA; resolves to z0r0z.eth. Also deployer of zRouter, zQuoter, LidoHarvester. |
gate (SLOWGate) |
0xb8B546b9...9ef3D4 (etherscan) |
Deployed via CREATE2 from the SLOW constructor; salt = bytes32(0). Holds posted relayer tips and routes settlements. |
htmlChunk1 |
0x745c9c91...21723e (etherscan) |
SSTORE2 storage contract holding the first ~24 KB of the on-chain dapp HTML. |
htmlChunk2 |
0x62a765fc...9863f8 (etherscan) |
SSTORE2 storage contract holding the second ~24 KB of the on-chain dapp HTML. |
| Vanity Factory | 0xac5fb2e1...DaC6Ea (etherscan) |
CREATE2-deployer factory used to mine the 0x00000000...888741… vanity prefix. |
Executive Summary
SLOW is a non-custodial timelock-and-co-sign wrapper for ETH and ERC-20 transfers. A sender depositTo(token, recipient, amount, delay, data) deposits funds into the contract, which mints an ERC-1155 wrapper to the recipient. The wrapper's id encodes the underlying token address in the low 160 bits and the delay (in seconds) in the upper 96 bits. While the timelock is in flight, the recipient cannot move the wrapper, the sender can reverse it, and after expiry the recipient can either unlock (move the wrapper into spendable balance) or claim (burn the wrapper and receive the raw underlying directly). If the recipient never settles, the sender can clawback 30 days after expiry.
A second optional layer is the per-user guardian. Calling setGuardian(addr) arms a co-sign requirement on every outflow from the caller's account: each safeTransferFrom and withdrawFrom must be pre-approved by the guardian via approveTransfer(from, transferId). Approvals are bound by nonces[from] and lastGuardianChange[from] so they cannot be replayed once the user spends or rotates their guardian. Rotating an active guardian is staged behind a 1-day veto window during which the user or the current guardian can cancel the rotation — this is the attempt to defeat a stolen-key takeover where the attacker would otherwise immediately remove the guardian.
A third optional layer is the relayer tip. depositToWithTip(...) posts an ETH tip to the companion SLOWGate contract. After the timelock expires, any keeper can call gate.claim(transferId) to settle and pocket the tip; payout still goes to the original recipient. The gate has no path that can redirect funds. If the underlying transfer settles by any other path before the keeper claims (recipient unlock, sender reverse, sender clawback), the tip is refundable to the depositor.
There is no admin, no owner, no pause, no upgrade path, and no fee. There is no global guardian. Each user controls their own state. The contract is immutable. Trust assumptions sit at the per-user level (whether to appoint a guardian, whether to use a delay, whether to post a tip), at the asset level (the contract assumes vanilla ERC-20 semantics — fee-on-transfer and rebasing tokens will diverge wrapper supply from reserves), and on the surrounding tooling (the deployer's published indexer / relayer software, which this contract does not constrain).
Architecture
graph TB
subgraph Users
Sender[Sender EOA]
Recipient[Recipient EOA]
Guardian[Optional Guardian EOA]
Keeper[Keeper / Relayer]
end
subgraph SLOWSystem["SLOW System"]
SLOW["SLOW<br/>0x00000000...feAaBC<br/>ERC-1155 wrapper + timelock + guardian"]
Gate["SLOWGate<br/>0xb8B546b9...9ef3D4<br/>CREATE2 child of SLOW<br/>holds tips, claim helper"]
Chunk1["htmlChunk1 SSTORE2<br/>0x745c9c91...21723e<br/>~24 KB HTML"]
Chunk2["htmlChunk2 SSTORE2<br/>0x62a765fc...9863f8<br/>~24 KB HTML"]
end
Token[Any ERC-20 token<br/>or native ETH]
Sender -->|depositTo / depositToWithTip| SLOW
SLOW -->|safeTransferFrom underlying| Token
SLOW -->|recordTip ETH| Gate
SLOW -->|read SSTORE2| Chunk1
SLOW -->|read SSTORE2| Chunk2
Recipient -->|unlock / claim / withdrawFrom / safeTransferFrom| SLOW
Sender -->|reverse / clawback| SLOW
Guardian -->|approveTransfer / revokeApproval / cancelGuardianChange| SLOW
Keeper -->|claim / claimMany| Gate
Gate -->|claimTipped| SLOW
SLOW -->|raw underlying to recipient| Recipient
Gate -->|tip to keeper| Keeper
System Overview
SLOW exposes three orthogonal mechanisms that compose freely:
- Time-locked escrow. Any address can call
depositToto wrap ETH or ERC-20 for a recipient with a delay. Until the delay passes, the wrapper is unmovable from the recipient's address (transfers of a delay-encoded id reset the timer; see thesafeTransferFromanalysis), and the sender can reverse the deposit. After the delay, the recipient settles viaunlock(wrapper → unlocked balance) orclaim(wrapper → burn + raw underlying out). - Per-user guardian co-sign. A user opts in by calling
setGuardian(addr). From that point on, everysafeTransferFromandwithdrawFromfrom that user must be pre-approved by the guardian for the exact transferId derived from the current(from, to, id, amount, nonce, lastGuardianChange, op-type)preimage. Rotation is staged behind a 1-day delay; first-time set is immediate. - Tipped relayer settlement.
depositToWithTipadditionally posts an ETH tip toSLOWGate. Any keeper can settle expired tipped transfers viagate.claimand collect the tip. The gate callsclaimTippedon SLOW, which performs the burn + payout to the original recipient.
What the contract intentionally does not do:
- It does not act as a custodian for arbitrary asset management. The only outflow paths are pre-committed payouts to the original recipient (
claim,claimTipped,unlockthenwithdrawFrom), reversal back to the sender (reverse,clawback), and tip flow on the gate. - It does not validate the underlying token. Fee-on-transfer, rebasing, and other non-vanilla ERC-20 implementations will leave wrapper supply diverged from real reserves.
- It does not enforce ERC-1155 batch semantics.
safeBatchTransferFromis wired to revert. Zero-amount transfers are also rejected.supportsInterfacestill reports ERC-1155, so this should be treated as ERC-1155-derived rather than fully compliant. - It does not implement a fee. There is no protocol cut on deposit, settlement, reversal, or tip.
Design Patterns Used
- ERC-1155 with composite ids:
id = uint256(uint160(token)) | (uint256(delay) << 160). Token address occupies the low 160 bits; the delay (in seconds, capped atuint96) occupies the upper 96 bits. This makes(token, delay)a single ERC-1155 token that wallets and indexers can render natively. - Preimage-based action ids with op-type separation:
transferId = keccak256(from, to, id, amount, nonces[from], lastGuardianChange[from], op-type). Two op-type bytes (_OP_TRANSFER = 0,_OP_WITHDRAW = 1) ensure a guardian approval for one cannot be consumed as the other at the same(from, to, id, amount). - Nonce-bound approvals: every state-changing wrapper op increments
nonces[from], so a guardian approval for the next op cannot be replayed once the user does anything else.lastGuardianChangeis mixed into the preimage so a guardian rotation atomically invalidates every dangling approval bound to the previous guardian. - Two-key staged rotation with mutual veto: rotating an active guardian creates a
pendingGuardianentry witheffectiveAt = now + 1 day. Either the user or the current guardian can cancel during the window; after the window, onlycommitGuardianis valid (anyone can call it). This is the attempt to defeat a stolen-key attacker who would otherwise immediately remove the guardian. - CREATE2 child as a permissioned helper:
SLOWGateis deployed in theSLOWconstructor withsalt = bytes32(0), so its address is determined entirely by the SLOW deployment address. The gate's only privileged entry into SLOW isclaimTipped, which the gate invokes only from_claimAndPayafter the gate has recorded a tip viarecordTip(which itself is callable only by SLOW). - Transient-storage reentrancy guard (EIP-1153): inherited from Solady's
ReentrancyGuardTransient. Cheaper than a permanent storage flag and automatically clears at transaction end. - On-chain dapp via SSTORE2: the contract's
html()view concatenates two ~24 KB SSTORE2 storage contracts to return the full SLOW dapp HTML. The frontend lives on Ethereum. - Multicall with
msg.valueguard: inherits Solady'sMulticallable, which reverts whenmsg.value != 0. Combined with the per-callmsg.valuechecks indepositTo/depositToWithTip, this prevents batched payable calls from reusing the same ETH across multiple deposits.
Access Control
Roles & Permissions
| ROLE | ASSIGNED BY | REVOKABLE | CALL COUNT |
|---|---|---|---|
| Sender (anyone) | N/A — permissionless | N/A | Unlimited |
| Recipient | Set by sender's to argument on deposit |
N/A | Unlimited |
| Guardian (per user) | setGuardian(addr) by the user |
Yes — setGuardian(0) (immediate first-time set is also possible after a previous removal) |
Unlimited |
| Keeper / Relayer (anyone via gate) | N/A — permissionless | N/A | Unlimited |
| Operator | Recipient's setApprovalForAll(operator, true) (inherited ERC-1155) |
Yes — setApprovalForAll(operator, false) |
Unlimited |
| Gate | Hardcoded immutable on deploy | No | Unlimited (only claimTipped) |
There is no owner, no admin, no pauser, no upgrader, no fee recipient.
Permission Matrix
| FUNCTION | SENDER | RECIPIENT | GUARDIAN | KEEPER | ANYONE |
|---|---|---|---|---|---|
depositTo |
☑ | ☑ | ☑ | ☑ | ☑ |
depositToWithTip |
☑ | ☑ | ☑ | ☑ | ☑ |
safeTransferFrom (own holdings) |
☑ | ☑ | ☒ | ☒ | ☒ |
withdrawFrom (own holdings) |
☑ | ☑ | ☒ | ☒ | ☒ |
unlock(transferId) |
☒ | ☑ (or operator) | ☒ | ☒ | ☒ |
claim(transferId) |
☒ | ☑ (or operator, only if no guardian on recipient) | ☒ | ☒ | ☒ |
claimTipped(transferId) |
☒ | ☒ | ☒ | ☒ via gate | ☒ via gate |
reverse(transferId) |
☑ (or operator) | ☒ | ☒ | ☒ | ☒ |
clawback(transferId) |
☑ (or operator), ≥30 days post-expiry | ☒ | ☒ | ☒ | ☒ |
setGuardian(addr) |
self only | self only | ☒ | ☒ | ☒ |
approveTransfer(from, id) |
☒ | ☒ | ☑ for that from |
☒ | ☒ |
revokeApproval(from, id) |
☒ | ☒ | ☑ for that from |
☒ | ☒ |
commitGuardian(user) |
☑ | ☑ | ☑ | ☑ | ☑ (after delay) |
cancelGuardianChange(user) |
self only or current guardian | — | ☑ | ☒ | ☒ |
gate.claim / gate.claimMany |
☑ | ☑ | ☑ | ☑ | ☑ |
gate.refundTip(transferId) |
☑ original tipper only | ☒ | ☒ | ☒ | ☒ |
safeBatchTransferFrom |
☒ (always reverts) | ☒ | ☒ | ☒ | ☒ |
Time Locks & Delays
| ACTION | TIME LOCK | CAN CANCEL | PURPOSE |
|---|---|---|---|
Settle a delayed transfer (unlock / claim / claimTipped) |
Yes — delay seconds set per deposit by the sender |
☑ Sender via reverse during the window |
Buyer's-remorse / mistake window for the sender; settlement cannot land sooner than the chosen delay |
| Sender clawback of a never-settled transfer | Yes — delay + 30 days past the deposit timestamp |
N/A | Recovery path for transfers to lost / dead recipients; bounded so legitimate recipients have a clear month-long window after expiry to act |
| Activate a first-time guardian | ☒ None | N/A | Immediate; symmetric with the same call also being able to remove a guardian |
| Rotate an active guardian | Yes — 1 day | ☑ User or current guardian via cancelGuardianChange; or by re-proposing the current guardian during the window |
Defeats a stolen-key takeover that would otherwise immediately remove the guardian |
| Approve a guardian-gated transferId | ☒ None | ☑ Same guardian via revokeApproval before consumption; auto-invalidated by any nonce bump or guardian rotation |
Lets the guardian pre-clear specific outflows |
| Refund a posted tip after non-gate settlement | ☒ None | N/A | Tip is refundable to the original tipper as soon as pendingTransfers[transferId].timestamp == 0 |
Economic Model
Funding Sources & Sinks
Inflows:
- ETH via
depositTo(0, to, 0, delay, data)withmsg.value > 0— the contract's ETH balance grows bymsg.value; an ERC-1155 wrapper for(ETH, delay)is minted toto. - ERC-20 via
depositTo(token, to, amount, delay, data)withmsg.value == 0— the contract pullsamountoftokenfrommsg.senderviasafeTransferFrom; an ERC-1155 wrapper for(token, delay)is minted toto. - ETH+tip via
depositToWithTip(0, to, amount, delay, tip, data)withmsg.value == amount + tip—amountbecomes the wrapped ETH;tipis forwarded toSLOWGate.recordTipand held by the gate. - ERC-20+tip via
depositToWithTip(token, to, amount, delay, tip, data)withmsg.value == tip—amountoftokenis pulled in;tip(ETH) is held by the gate.
Outflows:
- ETH via
claim/claimTipped/withdrawFromwhen the underlying is the zero address —pt.to.safeTransferETH(amount)(orto.safeTransferETH(amount)forwithdrawFrom). - ERC-20 via the same paths —
token.safeTransfer(pt.to, amount). - Tip ETH back to keeper via
gate._claimAndPay—msg.sender.safeTransferETH(t.amount)after a tipped settlement. - Tip ETH back to original tipper via
gate.refundTip— once the underlying transfer has cleared by any non-gate path.
Fee Structure
There is no protocol fee. Senders choose to post a tip (as ETH) only via depositToWithTip; that tip is paid to whichever keeper lands gate.claim(transferId) after expiry, or refunded to the original tipper if the underlying transfer clears by another path. SLOW itself collects nothing.
Economic Invariants
The contract attempts to maintain the following:
- ERC-1155 wrapper supply per id should equal the contract's underlying reserves of the encoded token, modulo unlocked balances awaiting
withdrawFrom. This holds only for vanilla ERC-20 tokens; fee-on-transfer and rebasing tokens will diverge. - Tip integrity:
depositToWithTiprequirestip != 0 && tip <= type(uint96).max, and the gate'srecordTipdefensively re-checksmsg.value <= type(uint96).maxto prevent a truncating cast from silently destroying value. msg.valueaccounting indepositToWithTip: ETH branch requires exactlyamount + tip; ERC-20 branch requires exactlytip. A mis-stated total reverts rather than over- or under-funding.- Multicall payable safety: Solady's
Multicallablereverts whenmsg.value != 0, so a single ETH attached to amulticall(bytes[])cannot be consumed by multiple payable deposits inside the batch. - Guardian approval is single-use:
delete guardianApproved[from][transferId]runs immediately before the burn / underlying-transfer, so each approval is consumed at most once. - Settlement is single-pass:
unlock,claim,claimTipped,reverse, andclawbackall begin withrequire(pt.timestamp != 0, TransferDoesNotExist())and end withdelete pendingTransfers[transferId], so a transferId cannot be settled twice.
Summary of Observations
SLOW is a small, focused contract — the SLOW + SLOWGate pair is ~1,000 lines of Solidity sitting on top of the Solady library. The design is consistent with "make a stolen private key recoverable, give yourself an undo button on sends, and let other people pay the gas to settle for you." The on-chain fingerprint matches that read.
Patterns that appear deliberate and self-consistent:
- ☑ No admin surface. There is no owner, no pauser, no fee recipient, no upgrade path. Every privileged action is per-user and revocable by that user.
- ☑ Op-type byte separation between transfer approvals and withdraw approvals. A guardian who pre-approved one cannot have it consumed as the other at the same
(from, to, id, amount). - ☑ Nonce +
lastGuardianChangemixed into the preimage. Approvals are single-use and atomically invalidated by guardian rotation, removing a class of stale-approval bugs. - ☑ Mutual veto on guardian rotation. Both the user and the current guardian can cancel a pending rotation during the 1-day window — this defeats a stolen-key attempt to immediately remove the guardian. After the window,
commitGuardianis permissionless and proposing the current guardian no longer cancels — the design treats the 1-day delay as a decision window, not an indefinite veto. - ☑
claimblocks when the recipient has a guardian. The auto-settle path that would skipunlockand pay raw underlying directly only runs for non-guarded recipients; guarded recipients must go throughunlock+withdrawFromso the raw exit inherits guardian gating. - ☑
claimTippedis gate-only. The internal_doClaimuses_burn(address(0), ...), which skips Solady's caller-auth check; both public callers (claimandclaimTipped) gate themselves explicitly. The code carries an inline note that any future caller of_doClaimmust enforce its own auth. - ☑
safeBatchTransferFromreverts and zero-amount transfers reject. This blocks two concrete dust-spam vectors: someone batch-transferring junk ids into your inbox, or filling your inbound-set with zero-amount delayed sends that you cannot meaningfully reverse. - ☑ Multicall's payable guard combined with per-call
msg.valuechecks closes the "batch payable deposits to reuse one ETH" exploit.
Trade-offs and trust assumptions:
- △ The contract assumes vanilla ERC-20 semantics. Fee-on-transfer and rebasing tokens will diverge wrapper supply from the underlying reserves and may leave late withdrawers unable to exit. The author calls this out in code comments and recommends using non-rebasing wrappers (e.g., wstETH) for rebasing assets — this is documentation, not enforcement.
- △ A hostile guardian can veto every rotation proposal indefinitely. The 1-day window is symmetric: it protects the user from a stolen-key attacker who would remove the guardian, but it also lets the guardian block every attempt the user makes to remove them. The author's framing in code comments is "appoint a guardian only if you trust them — that is what co-sign means." This is the intentional shape of the protection.
- △ A guardian-gated approval is bound to specific
(to, id, amount)— the guardian must verify intent off-chain. The op-split prevents cross-op consumption, not malicious approval of the wrong destination. - △ Inbound and outbound transfer sets (
_inboundTransfers,_outboundTransfers) are unbounded. Anyone can grow another user's_inboundTransfersby depositing tiny amounts to them. Iterating the sets viagetInboundTransfers/getOutboundTransferscan run out of gas at large sizes. The contract exposes paginated*At(i)getters and the author notes both observations in code comments. - △ EnumerableSetLib swaps with the last element on remove, so positional reads (
*At(i)) are not stable across settlement. Indexers should snapshot via the array getters or react to events; the contract notes this in comments. - △ Reverse and clawback move the wrapper back to
pt.fromvia_safeTransfer, which callsonERC1155Receivedon the sender. Contract senders that did not implementIERC1155Receivercannot be reversal- or clawback-eligible. EOAs are unaffected. - △ The post-rotation-window abort path is non-obvious: once
block.timestamp >= effectiveAt, onlycommitGuardianworks on the pending entry. To abort late, the user must propose a different guardian (which restarts the 1-day window) and then cancel during the new window. This is documented in code comments but worth highlighting because it diverges from a more common "user can always cancel before commit" pattern. - ◇ The on-chain HTML dapp is a content-only feature. It does not affect consensus or fund handling. It does mean the contract's bytecode permanently holds ~48 KB of presentation logic across two SSTORE2 contracts; rendering relies on the dapp's
marked/dompurifyJS imports being consistent with what the rendered HTML expects. - ◇
SLOWGatecarries aclaimManythat aborts the entire batch on the first failure. Keepers must filter ids off-chain (timelock-expired, no guardian on recipient, pending entry still present) before batching, or accept the all-or-nothing revert.
Activity at the time of analysis (2026-05-02, three days after deployment):
- 4 deposits totaling ~0.011 ETH; 3 settled via the gate (
claimTippedpaths), 1 reversed within minutes (a 0.006 ETH self-test send tovitalik.eththat was multicall-reversed by the depositor in the same minute). - Every transaction observed was sent by the deployer (z0r0z.eth). The contract has not yet seen third-party adoption.
- Wrapper supply is currently zero across every recipient observed; SLOW and Gate each hold zero ETH.
This document is produced for educational purposes. It reflects what the code does, not whether the code is safe to trust with funds or what it will do under all possible future configurations. Treat it as a starting point for your own review, not as a security audit.
References
| RESOURCE | NOTES |
|---|---|
| Etherscan — SLOW source | Verified source used as primary artifact |
| Etherscan — SLOWGate | CREATE2 child deployed by SLOW constructor |
| github.com/z0r0z/slow | Author's repo — context only; analysis is grounded in the on-chain verified source |
| Source Co — source.wei.is | Author's project space; positions SLOW as a building block above zFi |
| Solady — ERC1155 | Base ERC-1155 implementation inherited by SLOW |
| Solady — Multicallable | Reverts on nonzero msg.value; load-bearing for the deposit-batching safety property |
| Solady — ReentrancyGuardTransient | EIP-1153 transient-storage reentrancy lock |
| Solady — SSTORE2 | Used to read the on-chain dapp HTML chunks |
| EIP-1153 — Transient storage | Reentrancy guard primitive |
| zFi Project Overview | DNZN research index; SLOW is integrated by the zFi superdapp for delayed sends |
| zRouter Contract Analysis | Same author; sibling contract in the zFi ecosystem |
Change Log
| DATE | AUTHOR | NOTES |
|---|---|---|
| 2026-05-02 | Artificial. | Generated by robots. Gas: 110 tok |
| 2026-05-03 | Denizen. | Reviewed, edited, and curated by humans. |