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

ErrorSelectorWhen it fires, and what to do
Paused0x9e87fac8creating a link while new links are paused. Do: wait; undo, refund and claim still work
ZeroAmount0x1f2a2005msg.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
ZeroAddress0xd92e233dthe claim key, the claim recipient or the constructor's fee wallet is the zero address. Do: pass a real address
OverCap0x342fa66dthe amount is above ethCap, or above totalSupply * tokenCapBps / 10000. Do: send less, or split into several links
BadExpiry0xf4ad9a1fexpiresIn is under 10 minutes or over 90 days. Do: pick a value in range
Expired0x203d82d8a claim at or after expiresAt. Do: the sender can undo, or anyone can expireRefund
NotExpired0xd0404f85expireRefund before expiresAt. Do: wait for the deadline, or have the sender undo
NoLink0x0fb67a93no 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
LinkExists0xb2351eddcreating a link with an id this sender already used while it is still open. Do: use a new random id
BadSignature0x5cd5d233the 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
NotAdmin0x7bfa4b9fsetParams or transferAdmin from a non-admin. Do: use the admin address
FeeTooHigh0xcd4e6167feeBps above 100. Do: use 100 or less
BadCap0x7e042387tokenCapBps is 0 or above 10000, or ethCap is 0. Do: use a valid cap
NotAToken0x851f69d0the token address has no code, or totalSupply() or balanceOf() does not answer. Do: check the address and that it is an ERC-20
TransferFailed0x90b8ec18a token transfer or transferFrom failed or returned false, or an ETH payout was rejected. Do: see below
Reentrancy0xab143c06a 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 receive reverted. 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.