09 Contract
Errors
Every custom error of Oopsend.sol with its real 4-byte selector, when it fires, and what to do about it.
Oopsend uses Solidity custom errors, so a revert carries a 4-byte selector, the first four bytes of keccak256("Name()"). None of the errors takes arguments. The selectors below were computed from the same table the lab uses, and the Solidity suite asserts that four of them match (BadSignature, NoLink, OverCap, Expired).
#Contract errors
| Error | Selector | When it fires, and what to do |
|---|---|---|
Paused | 0x9e87fac8 | creating a link while new links are paused. Do: wait; undo, refund and claim still work |
ZeroAmount | 0x1f2a2005 | msg.value or amount is zero, nothing arrived from a token, or sweepFees has nothing accrued. Do: send a positive amount; sweep only when feesAccrued(token) is above zero |
ZeroAddress | 0xd92e233d | the claim key, the claim recipient or the constructor's fee wallet is the zero address. Do: pass a real address |
OverCap | 0x342fa66d | the amount is above ethCap, or above totalSupply * tokenCapBps / 10000. Do: send less, or split into several links |
BadExpiry | 0xf4ad9a1f | expiresIn is under 10 minutes or over 90 days. Do: pick a value in range |
Expired | 0x203d82d8 | a claim at or after expiresAt. Do: the sender can undo, or anyone can expireRefund |
NotExpired | 0xd0404f85 | expireRefund before expiresAt. Do: wait for the deadline, or have the sender undo |
NoLink | 0x0fb67a93 | no open record under that key: wrong sender or id, or already claimed, undone or refunded. Do: check the link; if it was closed, read the events |
LinkExists | 0xb2351edd | creating a link with an id this sender already used while it is still open. Do: use a new random id |
BadSignature | 0x5cd5d233 | the claim signature is malformed, malleable, from the wrong key, for another recipient, another link, another chain or another contract. Do: sign Claim(linkKey, recipient) with the link's key, under the right domain |
NotAdmin | 0x7bfa4b9f | setParams or transferAdmin from a non-admin. Do: use the admin address |
FeeTooHigh | 0xcd4e6167 | feeBps above 100. Do: use 100 or less |
BadCap | 0x7e042387 | tokenCapBps is 0 or above 10000, or ethCap is 0. Do: use a valid cap |
NotAToken | 0x851f69d0 | the token address has no code, or totalSupply() or balanceOf() does not answer. Do: check the address and that it is an ERC-20 |
TransferFailed | 0x90b8ec18 | a token transfer or transferFrom failed or returned false, or an ETH payout was rejected. Do: see below |
Reentrancy | 0xab143c06 | a call re-entered the contract while it was running. Do: not expected in normal use |
#About TransferFailed
It is the contract's only signal for a failed movement of value, so it has several causes:
- Creating a token link: the sender has not approved enough, has too little balance, or the token blocked the transfer.
- Claiming or undoing in ETH: the receiving address is a contract whose
receivereverted. For a claim, choose another recipient; the link stays open. For an undo or refund, the sender is a contract that cannot receive ETH. The funds stay in the escrow until the sender address can receive them. - Paying a token: the token refused to pay, for example because the recipient is blocklisted.
Because a failed payout reverts the whole call, the record stays open and nothing is lost.
#Errors that exist only in the simulation
The JavaScript engine has two extra names for conditions that, on a real chain, would come from the token contract and surface as TransferFailed on Oopsend's side: InsufficientBalance (0xf4d678b8) and InsufficientAllowance (0x13be252b). These selectors are only computed by the simulation. Do not look for them on chain.
#Reading a revert
With ethers v6, custom errors decode automatically if the ABI includes them:
try { await oops.claim(sender, id, recipient, sig); }
catch (e) { console.log(e.revert?.name ?? e.shortMessage); } // e.g. "BadSignature"
With cast:
cast 4byte 0x5cd5d233 # looks up a selector
cast sig "BadSignature()" # prints 0x5cd5d233
#Framework-level failures
A transaction can also fail before Oopsend runs: not enough ETH for gas, a wrong nonce, or a malformed call. Those errors come from the node or the wallet and have no Oopsend selector.