Integrate
QuaiRelay has no HTTP API — every integration talks straight to the contract and to the public
Quai RPC. All examples use quais, checksum every address before it reaches the RPC, and treat
an unset gas price/balance as unknown rather than zero (the same rules the relay's own front
ends follow).
Send QUAI through the relay
import { quais } from 'quais';
const RPC_URL = 'https://rpc.quai.network/cyprus1';
const RELAY_ADDRESS = '0x0031B09e63592B713768761cdDACC603974c10bD';
const RELAY_ABI = ['function sendTo(address payable to) external payable'];
// quais.getAddress() checksums — Quai's RPC rejects a lowercase address outright.
async function sendThroughRelay(signer, destination, valueWei) {
const to = quais.getAddress(String(destination).toLowerCase());
if (!to.startsWith('0x00')) {
throw new Error('destination is not a Cyprus-1 address — QUAI sent there is unreachable');
}
const relay = new quais.Contract(RELAY_ADDRESS, RELAY_ABI, signer);
const tx = await relay.sendTo(to, { value: valueWei });
await tx.wait();
return tx.hash;
}Read balance + gas price to offer "send everything"
import { quais } from 'quais';
const provider = new quais.JsonRpcProvider('https://rpc.quai.network/cyprus1', undefined, {
usePathing: false, // RPC_URL already ends in /cyprus1 — do not let the SDK append it again
});
async function maxSendable(accountAddress) {
const address = quais.getAddress(String(accountAddress).toLowerCase());
const [balance, feeData] = await Promise.all([
provider.getBalance(address),
provider.getFeeData(quais.Zone.Cyprus1), // getFeeData needs the zone on Quai
]);
const gasLimit = 60_000n; // a sendTo call; re-estimated by the wallet when it signs
const gasPrice = BigInt(feeData.gasPrice ?? feeData.maxFeePerGas ?? 0n);
const gasCost = (gasLimit * gasPrice * 125n) / 100n; // 25% headroom
if (gasCost >= balance) return 0n; // balance can't cover its own fee
return balance - gasCost;
}Raw JSON-RPC (no wallet SDK)
Useful for a script that just wants to read the relay's bytecode or watch its events — no signing involved.
POST https://rpc.quai.network/cyprus1
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"quai_getCode","params":["0x0031B09e63592B713768761cdDACC603974c10bD","latest"]}Returns the deployed runtime bytecode (non-0x response = the contract exists at that address).
POST https://rpc.quai.network/cyprus1
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"quai_getBalance","params":["0x<your-checksummed-address>","latest"]}Calldata by hand
If you need to build the transaction yourself instead of using quais.Contract:
// selector for sendTo(address), keccak-256("sendTo(address)")[0..4]
const SELECTOR = '0xe6d25245';
function encodeSendTo(toChecksummed) {
const body = toChecksummed.slice(2).toLowerCase().padStart(64, '0');
return SELECTOR + body;
}
const tx = {
from: accountAddress,
to: '0x0031B09e63592B713768761cdDACC603974c10bD',
value: '0x' + valueWei.toString(16),
data: encodeSendTo(destinationChecksummed),
gas: '0x' + (60_000n).toString(16),
};
// wallet.request({ method: 'quai_sendTransaction', params: [tx] })