Skip to content

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
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 depositTo to 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 the safeTransferFrom analysis), and the sender can reverse the deposit. After the delay, the recipient settles via unlock (wrapper → unlocked balance) or claim (wrapper → burn + raw underlying out).
  • Per-user guardian co-sign. A user opts in by calling setGuardian(addr). From that point on, every safeTransferFrom and withdrawFrom from 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. depositToWithTip additionally posts an ETH tip to SLOWGate. Any keeper can settle expired tipped transfers via gate.claim and collect the tip. The gate calls claimTipped on 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, unlock then withdrawFrom), 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. safeBatchTransferFrom is wired to revert. Zero-amount transfers are also rejected. supportsInterface still 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 at uint96) 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. lastGuardianChange is 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 pendingGuardian entry with effectiveAt = now + 1 day. Either the user or the current guardian can cancel during the window; after the window, only commitGuardian is 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: SLOWGate is deployed in the SLOW constructor with salt = bytes32(0), so its address is determined entirely by the SLOW deployment address. The gate's only privileged entry into SLOW is claimTipped, which the gate invokes only from _claimAndPay after the gate has recorded a tip via recordTip (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.value guard: inherits Solady's Multicallable, which reverts when msg.value != 0. Combined with the per-call msg.value checks in depositTo / 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) with msg.value > 0 — the contract's ETH balance grows by msg.value; an ERC-1155 wrapper for (ETH, delay) is minted to to.
  • ERC-20 via depositTo(token, to, amount, delay, data) with msg.value == 0 — the contract pulls amount of token from msg.sender via safeTransferFrom; an ERC-1155 wrapper for (token, delay) is minted to to.
  • ETH+tip via depositToWithTip(0, to, amount, delay, tip, data) with msg.value == amount + tipamount becomes the wrapped ETH; tip is forwarded to SLOWGate.recordTip and held by the gate.
  • ERC-20+tip via depositToWithTip(token, to, amount, delay, tip, data) with msg.value == tipamount of token is pulled in; tip (ETH) is held by the gate.

Outflows:

  • ETH via claim / claimTipped / withdrawFrom when the underlying is the zero address — pt.to.safeTransferETH(amount) (or to.safeTransferETH(amount) for withdrawFrom).
  • ERC-20 via the same paths — token.safeTransfer(pt.to, amount).
  • Tip ETH back to keeper via gate._claimAndPaymsg.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: depositToWithTip requires tip != 0 && tip <= type(uint96).max, and the gate's recordTip defensively re-checks msg.value <= type(uint96).max to prevent a truncating cast from silently destroying value.
  • msg.value accounting in depositToWithTip: ETH branch requires exactly amount + tip; ERC-20 branch requires exactly tip. A mis-stated total reverts rather than over- or under-funding.
  • Multicall payable safety: Solady's Multicallable reverts when msg.value != 0, so a single ETH attached to a multicall(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, and clawback all begin with require(pt.timestamp != 0, TransferDoesNotExist()) and end with delete 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 + lastGuardianChange mixed 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, commitGuardian is permissionless and proposing the current guardian no longer cancels — the design treats the 1-day delay as a decision window, not an indefinite veto.
  • claim blocks when the recipient has a guardian. The auto-settle path that would skip unlock and pay raw underlying directly only runs for non-guarded recipients; guarded recipients must go through unlock + withdrawFrom so the raw exit inherits guardian gating.
  • claimTipped is gate-only. The internal _doClaim uses _burn(address(0), ...), which skips Solady's caller-auth check; both public callers (claim and claimTipped) gate themselves explicitly. The code carries an inline note that any future caller of _doClaim must enforce its own auth.
  • safeBatchTransferFrom reverts 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.value checks 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 _inboundTransfers by depositing tiny amounts to them. Iterating the sets via getInboundTransfers / getOutboundTransfers can 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.from via _safeTransfer, which calls onERC1155Received on the sender. Contract senders that did not implement IERC1155Receiver cannot be reversal- or clawback-eligible. EOAs are unaffected.
  • △ The post-rotation-window abort path is non-obvious: once block.timestamp >= effectiveAt, only commitGuardian works 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 / dompurify JS imports being consistent with what the rendered HTML expects.
  • SLOWGate carries a claimMany that 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 (claimTipped paths), 1 reversed within minutes (a 0.006 ETH self-test send to vitalik.eth that 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.