0x000000000000888741B254d37e1b27128AfEAaBC
SLOW is a protocol that adds safety mechanisms to token transfers through two powerful features:
- Timelock: Enforces a waiting period before recipients can access transferred tokens
- Guardian: Optional trusted party that can approve or block transfers
Think of it as a security-enhanced way to transfer ETH and ERC20 tokens with built-in protection mechanisms.
- Prevent Theft: Even if your keys are compromised, attackers must wait for the timelock to expire
- Reverse Mistakes: Cancel erroneous transfers before the timelock expires
- Secure High-Value Transactions: Add guardian approval for extra security
- Schedule Transfers: Set timelock periods from seconds to days or longer
- Wrap and send ETH or any ERC20 token
- Set custom timelock periods for each transfer
- Appoint a guardian to approve sensitive transfers
- Reverse pending transfers before timelock expiry
- Visually track tokens with dynamic SVG renders
- Multicall support for batched operations
SLOW uses the ERC1155 token standard to represent wrapped tokens with timelock and guardian protections:
- Tokenization: Each base token (ETH or ERC20) is wrapped into a SLOW token
- Token ID Encoding: The token ID encodes both the token address and timelock period
- Balance States: Token balances exist in two states - locked and unlocked
- Transfer Flow: Transfers go through a predictable lifecycle with safety checks
graph TD
A[User deposits tokens] --> B[Tokens wrapped as SLOW tokens]
B --> C{Set timelock?}
C -->|Yes| D[Create pending transfer with timelock]
C -->|No| E[Tokens immediately unlocked]
D -->|Wait for timelock| F[Manually unlock tokens]
D -->|Before expiry| G[Optional: Reverse transfer]
F --> H[Tokens available for withdrawal]
E --> H
H --> I[Withdraw tokens to recipient]
stateDiagram-v2
[*] --> Deposit: User deposits tokens
Deposit --> Locked: With timelock
Deposit --> Unlocked: Without timelock
Locked --> PendingTransfer: During timelock period
PendingTransfer --> Reversed: Before timelock expires
PendingTransfer --> ReadyToUnlock: After timelock expires
ReadyToUnlock --> Unlocked: User calls unlock()
Unlocked --> Withdrawn: User withdraws tokens
Reversed --> [*]
Withdrawn --> [*]
Alice wants to send 1 ETH to Bob with a 24-hour timelock:
-
Alice calls
depositTo
with parameters:- token: 0x0000000000000000000000000000000000000000 (ETH)
- to: Bob's address
- amount: 1 ETH
- delay: 86400 (seconds in 24 hours)
-
The contract:
- Creates a unique transferId
- Records a pending transfer with the current timestamp
- Mints a SLOW token to Bob representing the locked 1 ETH
-
After 24 hours, Bob calls
unlock(transferId)
to move the tokens to his unlocked balance -
Bob can now call
withdrawFrom
to get the actual ETH
Charlie sets up a guardian for extra security:
-
Charlie calls
setGuardian(guardianAddress)
to designate a trusted guardian -
When Charlie wants to transfer tokens, the transfer requires guardian approval
-
Charlie initiates a transfer to Dave with
safeTransferFrom
-
The guardian calls
approveTransfer(Charlie's address, transferId)
to approve -
If the transfer had a timelock, Dave still needs to wait and then unlock it
-
Without guardian approval, the transfer remains pending indefinitely
Emma accidentally sends tokens to the wrong address:
-
Emma initiates a transfer with a 48-hour timelock
-
Emma realizes the mistake and calls
reverse(transferId)
before the timelock expires -
The tokens are returned to Emma's address
depositTo(token, to, amount, delay, data)
- Deposit tokens and create a timelockwithdrawFrom(from, to, id, amount)
- Withdraw unlocked tokenssafeTransferFrom(from, to, id, amount, data)
- Transfer tokens with security checksunlock(transferId)
- Unlock tokens after timelock expiry
setGuardian(guardian)
- Designate an address as your guardianapproveTransfer(from, transferId)
- Guardian approves a pending transfer
predictTransferId(from, to, id, amount)
- Calculate the ID for a pending transferreverse(transferId)
- Cancel a pending transfer before timelock expiryencodeId(token, delay)
- Create a token ID from token address and timelockdecodeId(id)
- Extract token address and timelock from token ID
Each SLOW token ID encodes two pieces of information:
- Lower 160 bits: The underlying token address (0x0 for ETH)
- Upper 96 bits: The timelock delay in seconds
|---- 96 bits ----|---- 160 bits ----|
| Timelock | Token Address |
Each transfer gets a unique ID generated from:
keccak256(abi.encodePacked(from, to, id, amount, nonces[from]))
This ensures each transfer can be uniquely identified and tracked.
- Guardian Cooldown: 1 day cooldown between guardian changes prevents flash attacks
- Reversible Transfers: Only possible before timelock expiry
- Reentrancy Protection: External-facing functions protected against reentrancy attacks
- Balance Tracking: Strict accounting of locked vs. unlocked balances
Run: curl -L https://foundry.paradigm.xyz | bash && source ~/.bashrc && foundryup
Build the foundry project with forge build
. Run tests with forge test
. Measure gas with forge snapshot
. Format with forge fmt
.
Contracts will be tested and gas measured on every push and pull request. You can edit the CI script in .github/workflows/ci.yml.
lib
├─ forge-std — https://github.com/foundry-rs/forge-std
src
├─ SLOW — Protocol Contract
test
└─ SLOW.t - Test Protocol Contract
These smart contracts and testing suite are being provided as is. No guarantee, representation or warranty is being made, express or implied, as to the safety or correctness of anything provided herein or through related user interfaces. This repository and related code have not been audited and as such there can be no assurance anything will work as intended, and users may experience delays, failures, errors, omissions, loss of transmitted information or loss of funds. The creators are not liable for any of the foregoing. Users should proceed with caution and use at their own risk.
See LICENSE for more details.