Contract reference
QuaiRelay
0x0031B09e63592B713768761cdDACC603974c10bD
— Quai mainnet, Cyprus-1. solc 0.8.20, EVM target paris. 600-byte runtime, confirmed live via
quai_getCode.
Purpose. Forward exactly msg.value to a caller-supplied recipient, in the same transaction,
so a wallet that can call a contract but can't make a plain value transfer can still move its
QUAI out. It holds nothing between calls.
Function
function sendTo(address payable to) external payable;Forwards the attached QUAI to to. Reverts the whole transaction — nothing is sent, nothing is
kept — if any of these hold:
| Condition | Revert reason |
|---|---|
msg.value == 0 | "Zero amount" |
to == address(0) | "Zero recipient" |
to == address(this) | "Recipient is the relay" |
to is not a Cyprus-1 address (uint160(to) >> 152 != 0, i.e. first byte isn't 0x00) | "Recipient not Cyprus-1" |
the forwarded call to to fails (e.g. to is a contract with no payable fallback) | "Payout failed" |
There is deliberately no receive() function — a bare transfer sent directly to the relay
address (not through sendTo) reverts rather than being accepted and stranded.
Event
event Relayed(address indexed from, address indexed to, uint256 amount);Emitted once, after the forward succeeds. from is msg.sender (the caller, i.e. the wallet
that signed the transaction), to and amount are exactly what was requested and paid.
Selector
sendTo(address) → 0xe6d25245 (keccak-256 of the signature, asserted against the deployed
bytecode's dispatcher in the repo's tests — never hand-typed as trusted).
Trust model
There is nothing to trust beyond reading the ~30 lines of source, by construction:
- No owner, no admin. There is no privileged address anywhere in the contract — no
owner, noonlyOwner, no role that can change behavior. - No storage. The contract writes zero storage slots across its lifetime (asserted in
test/QuaiRelay.test.cjs). It cannot accumulate state that a future call could read or act on. - No upgrade path, no
selfdestruct. The deployed bytecode is final; it can never be swapped for different logic. - Never holds funds. The only payable path (
sendTo) forwards the value before returning, and reverts the whole transaction if the forward fails. The absence ofreceive()means a stray direct transfer bounces instead of getting stuck in a contract with no withdraw function. - The recipient is chosen by the caller, in the same call it pays into. Nothing is
remembered between transactions, and no other account's call can redirect a QUAI already in
flight — each
sendTois self-contained.
What it cannot protect you from: naming the wrong recipient. That's true of any transfer, and the contract has no way to distinguish a typo from an intended payment — it only refuses recipients that are provably lossy (zero address, itself, another zone).