Storage Layout
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
| METADATA | |
|---|---|
| Contract Address | 0x00000000...feAaBC (etherscan) |
| Network | Ethereum Mainnet |
| Analysis Date | 2026-05-02 |
Variables
SLOW — Persistent Storage (declaration order)
Slot indices below follow Solidity's declaration order. Mappings consume one slot for the base hash; entries are computed at keccak256(key . slot). Solady's ERC1155 base contract does not use sequential slots — it stores _balances, _operatorApprovals, and the URI under fixed keccak256 namespaces, so they do not collide with SLOW's sequentially-allocated slots below.
| SLOT | VARIABLE NAME | TYPE | CURRENT VALUE / NOTES | PURPOSE |
|---|---|---|---|---|
| 0 | guardianApproved |
mapping(address => mapping(uint256 => bool)) |
empty for all observed users | Per-user table of pre-approved transferIds. Set by approveTransfer, cleared on consumption or by revokeApproval. |
| 1 | _outboundTransfers |
mapping(address => EnumerableSetLib.Uint256Set) |
empty for all observed users | Set of currently-pending transferIds where the user is the sender. Used by getOutboundTransfers / outboundTransferAt. |
| 2 | _inboundTransfers |
mapping(address => EnumerableSetLib.Uint256Set) |
empty for all observed users | Set of currently-pending transferIds where the user is the recipient. Anyone can grow this set on another user via dust deposits; no ACL on the deposit recipient. |
| 3 | unlockedBalances |
mapping(address => mapping(uint256 => uint256)) |
0 for all observed (user, id) pairs |
Spendable wrapper balances per (user, id) — credited on unlock (post-expiry), reverse, clawback, and immediate (delay=0) deposits. Decremented on withdrawFrom and safeTransferFrom. |
| 4 | pendingTransfers |
mapping(uint256 => PendingTransfer) |
0 for all observed transferIds | Live escrow records keyed by transferId. Struct: (uint96 timestamp, address from, address to, uint256 id, uint256 amount). |
| 5 | lastGuardianChange |
mapping(address => uint256) |
0 for all observed users | Timestamp of the user's last setGuardian (first-time set) or commitGuardian (rotation). Mixed into every transferId preimage so a guardian rotation atomically invalidates dangling approvals. |
| 6 | pendingGuardian |
mapping(address => PendingGuardian) |
0 for all observed users | Staged guardian rotation. Struct: (address guardian, uint96 effectiveAt). effectiveAt != 0 means a rotation is in flight. |
| 7 | guardians |
mapping(address => address) |
0 for all observed users | Currently active guardian per user. Zero means no guardian is set. |
| 8 | nonces |
mapping(address => uint256) |
4 for 0x1C0Aa8cC...855A20 (deployer); 0 everywhere else observed |
Per-user counter incremented on every nonce-bumping op (safeTransferFrom with delay or guardian, withdrawFrom with guardian, delayed _finishDeposit). Mixed into transferId preimages to make approvals single-use. |
SLOW — Immutable Storage (deploy-time constants in code)
| NAME | TYPE | VALUE | PURPOSE |
|---|---|---|---|
gate |
address (public) |
0xb8B546b9...9ef3D4 (etherscan) |
CREATE2-deployed SLOWGate companion. Sole authorized caller of claimTipped. |
htmlChunk1 |
address (internal) |
0x745c9c91...21723e (etherscan) |
First SSTORE2 chunk of the on-chain dapp HTML. Read by html(). |
htmlChunk2 |
address (internal) |
0x62a765fc...9863f8 (etherscan) |
Second SSTORE2 chunk. Concatenated with htmlChunk1 in html(). |
SLOW — Compile-time Constants
| NAME | VALUE | PURPOSE |
|---|---|---|
_GUARDIAN_CHANGE_DELAY |
1 days (86400 s) |
Veto window for guardian rotation. |
_CLAWBACK_GRACE |
30 days (2592000 s) |
Wait after timelock expiry before sender can clawback. |
_OP_TRANSFER |
0 |
Op-type byte mixed into the preimage of safeTransferFrom transferIds. |
_OP_WITHDRAW |
1 |
Op-type byte mixed into the preimage of withdrawFrom transferIds. |
SLOW — Inherited Storage (Solady)
The Solady ERC-1155 base does not use sequential slot allocation; it derives slot keys from fixed namespaces to avoid colliding with the inheriting contract's own variables. Notable inherited storage:
_balances[id][owner]— ERC-1155 balance table (used for the wrapper supply per id)._operatorApprovals[owner][operator]— ERC-1155 operator-approval flag (used byunlock,claim,reverse,clawbackto checkisApprovedForAll).- The inherited
MulticallableandReentrancyGuardTransientkeep no persistent storage; the latter uses transient storage.
SLOW — Transient Storage (TSTORE / TLOAD, EIP-1153)
| SLOT | PURPOSE |
|---|---|
| Solady-derived | Reentrancy lock used by every nonReentrant external entry point (setGuardian, unlock, claim, claimTipped, depositTo, depositToWithTip, withdrawFrom, safeTransferFrom, reverse, clawback). Cleared automatically at transaction end. |
SLOWGate — Persistent Storage
| SLOT | VARIABLE NAME | TYPE | CURRENT VALUE / NOTES | PURPOSE |
|---|---|---|---|---|
| 0 | tips |
mapping(uint256 => Tip) |
empty (no tips currently held) | Posted relayer tips keyed by transferId. Struct: (uint96 amount, address sender) — packs into one slot. |
SLOWGate — Immutable Storage
| NAME | TYPE | VALUE | PURPOSE |
|---|---|---|---|
slow |
SLOW (public) |
0x00000000...feAaBC (etherscan) (the parent contract) |
Set in the gate constructor as SLOW(msg.sender) — locked to the address that deployed it. |
Diagrams
graph TB
subgraph SLOWStorage["SLOW Persistent Storage (declaration order)"]
S0["Slot 0: guardianApproved<br/>mapping(user → mapping(transferId → bool))"]
S1["Slot 1: _outboundTransfers<br/>mapping(user → Uint256Set)"]
S2["Slot 2: _inboundTransfers<br/>mapping(user → Uint256Set)<br/>(can be grown by anyone via dust deposits)"]
S3["Slot 3: unlockedBalances<br/>mapping(user → mapping(id → uint256))"]
S4["Slot 4: pendingTransfers<br/>mapping(transferId → PendingTransfer)"]
S5["Slot 5: lastGuardianChange<br/>mapping(user → uint256)"]
S6["Slot 6: pendingGuardian<br/>mapping(user → PendingGuardian)"]
S7["Slot 7: guardians<br/>mapping(user → address)"]
S8["Slot 8: nonces<br/>mapping(user → uint256)"]
end
subgraph SLOWImmut["SLOW Immutables"]
Gate["gate → 0xb8B546b9...9ef3D4"]
Chunk1["htmlChunk1 → 0x745c9c91...21723e (SSTORE2)"]
Chunk2["htmlChunk2 → 0x62a765fc...9863f8 (SSTORE2)"]
end
subgraph SoladyInherit["Inherited (Solady, fixed namespaces)"]
Bal["_balances[id][owner] (ERC-1155 supply)"]
Op["_operatorApprovals[owner][operator]"]
end
subgraph GateStorage["SLOWGate Storage"]
T0["Slot 0: tips<br/>mapping(transferId → Tip(uint96 amount, address sender))"]
SlowImm["immutable slow → SLOW (parent)"]
end
subgraph Transient["Transient Storage (EIP-1153, Solady)"]
Reent["nonReentrant lock (cleared at tx end)"]
end
Gate -.->|deployed via CREATE2 from SLOW constructor| GateStorage
style S4 fill:#ffe1e1
style S7 fill:#fff4e1
style S8 fill:#fff4e1
style T0 fill:#e1f5ff
style Reent fill:#e1f5ff
Composite Id Encoding
The ERC-1155 token id encodes both the underlying token address and the timelock delay:
bit: 255 160 159 0
| | | |
| delay (96 bits, seconds, uint96) | | token address (160 bits; 0 = native ETH) |
Round-trip helpers:
encodeId(token, delay) = uint256(uint160(token)) | (uint256(delay) << 160)decodeId(id) = (address(uint160(id)), id >> 160)
delay == 0 means the wrapper mints unlocked (no escrow row created); delay > 0 mints the wrapper to to and creates a pendingTransfers[transferId] row simultaneously.
Read-Verification Commands
C=0x000000000000888741B254d37e1b27128AfEAaBC
G=0xb8B546b93a82f4Aa6f0345142dF5679B659ef3D4
# IMMUTABLES & METADATA
cast call $C "gate()(address)"
cast call $C "name()(string)"
cast call $C "symbol()(string)"
# PUBLIC MAPPING GETTERS (per-user reads — substitute USER)
USER=0x1C0Aa8cCD568d90d61659F060D1bFb1e6f855A20
cast call $C "nonces(address)(uint256)" $USER
cast call $C "guardians(address)(address)" $USER
cast call $C "lastGuardianChange(address)(uint256)" $USER
cast call $C "pendingGuardian(address)(address,uint96)" $USER
# PENDING TRANSFER LOOKUP (per transferId)
TID=0x...
cast call $C "pendingTransfers(uint256)(uint96,address,address,uint256,uint256)" $TID
# OUTBOUND / INBOUND TRANSFER ENUMERATION
cast call $C "outboundTransferCount(address)(uint256)" $USER
cast call $C "inboundTransferCount(address)(uint256)" $USER
cast call $C "outboundTransferAt(address,uint256)(uint256)" $USER 0
cast call $C "inboundTransferAt(address,uint256)(uint256)" $USER 0
cast call $C "getOutboundTransfers(address)(uint256[])" $USER # bounded by gas — paginate large sets
cast call $C "getInboundTransfers(address)(uint256[])" $USER # same caveat
# GATE STATE
cast call $G "slow()(address)"
cast call $G "tips(uint256)(uint96,address)" $TID
# CONTRACT BALANCES
cast balance $C
cast balance $G