Hartii developer docs

QuaiRelay

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

solidity
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:

ConditionRevert 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

solidity
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, no onlyOwner, 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 of receive() 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 sendTo is 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).

Quai Network mainnet · chain 9 · Cyprus-1. Figures marked "read on" a date were read from the chain that day; re-read before relying on them.