Hartii developer docs

QuaiRelay

QuaiRelay

QuaiRelay solves one narrow problem: some in-app wallets can call a contract but cannot make a plain value transfer (on Quai this typically surfaces to the user as could not coalesce error). QUAI sitting in such a wallet is otherwise stuck. QuaiRelay turns a transfer into a contract call — the wallet signs sendTo(recipient) with value attached, and the relay forwards that exact value to recipient in the same transaction.

text
wallet ──sendTo(recipient) + value──▶ QuaiRelay ──same transaction──▶ recipient

Architecture

QuaiRelay is a contract, not a service. There is no backend, no database, no indexer and no owner/operator key. The two front ends that call it are:

  • quai-relay — the standalone page. index.html + app.js + relay-core.js, no build step, no dependencies, served statically (including from IPFS). Connects to the wallet's injected provider, reads balance/gas from the public Quai RPC, and submits one transaction.
  • /blipsend on HartiiLabs (hartii-labs, a separate product — see its own docs). A production page aimed at Blippay in-app-wallet users, offering the same plain-transfer-first / contract-fallback pattern, wired to the same relay contract. It is documented here only because it is QuaiRelay's main real-world caller — see How /blipsend uses the relay below.

Data flow for a relay send:

  1. The page reads the connected wallet's balance and the current gas price directly from https://rpc.quai.network/cyprus1 (quai_getBalance, quai_gasPrice).
  2. It validates the destination client-side (Cyprus-1 prefix, checksum, not self, not the relay) and computes the maximum sendable amount (balance minus gas cost with 25% headroom).
  3. The wallet signs a transaction: to = the relay contract, value = the amount, data = ABI-encoded sendTo(recipient).
  4. The relay contract forwards msg.value to recipient in the same transaction, or reverts the whole thing — there is no partial-failure state.

Live contract

NetworkAddress
Quai mainnet, Cyprus-1 (chain 9)0x0031B09e63592B713768761cdDACC603974c10bD

Verified live: quai_getCode against this address on https://rpc.quai.network/cyprus1 returns 600 bytes of deployed runtime bytecode (matches deployments.json's recorded runtimeBytes: 600 in the quai-relay repo). Deployed 2026-09-19, solc 0.8.20, EVM paris.

See contracts for the full interface and trust model, and integrate for runnable examples.

How /blipsend uses the relay

HartiiLabs' /blipsend page (hartii-labs/src/pages/BlipSend.jsx) offers two ways to move QUAI out of a stuck wallet, both to the same address the visitor typed:

  1. Plain transfer — an ordinary signer.sendTransaction({ to, value }). Works for any wallet that can send.
  2. Contract route (QuaiRelay) — for wallets that can't: contract.sendTo(destination, { value }) against the relay address above, using the ABI in src/abi/quaiRelay.js.

The page's own comment explains why it uses the general-purpose relay rather than a fixed-destination forwarder contract (hartii-labs also has one, QuaiForwarder, used for a single private payee elsewhere on the site):

In other words: sendTo's lack of a fixed destination isn't a limitation, it's the reason this contract is safe to expose on a public page at all.

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.