Skip to content

Relayer Reference

The Ryze Relayer is an intent-based transaction execution service. Instead of users submitting on-chain transactions directly, they sign an intent (an EIP-712 typed message) and the relayer batches and executes it on their behalf -- handling gas, oracle price data, and batching for efficiency.

Base URLs

EnvironmentBase URL
Mainnethttps://mainnet.relayer.ryze.pro/api/v1
Testnethttps://sepolia.relayer.ryze.pro/api/v1

Endpoints

MethodPathDescription
POST/intents/submitSubmit a signed intent (swap, join, exit)
GET/intents/:intentIdGet intent execution status
GET/healthService health check

Intent Types

All intents go through the same /intents/submit endpoint. The type field determines the operation:

TypeDescriptionGuide
swapToken-to-token swap (single or multi-hop)Submit Swap
joinSingle-asset liquidity depositSubmit Join
joinProportionalMulti-asset proportional liquidity depositSubmit Proportional Join
exitProportionalProportional liquidity withdrawalSubmit Exit

How It Works

1. User gets a quote from the Router
2. User approves the MultiHopRouter to spend their tokens (ERC-20 approve)
3. User signs an EIP-712 intent message with their wallet
4. Frontend POSTs the signed intent to the Relayer
5. Relayer validates signature + nonce, queues the intent
6. Relayer batches intents, fetches oracle prices, executes on-chain
7. Frontend polls for status until confirmed

Authentication

No API keys. The relayer authenticates via EIP-712 signatures -- each intent must be signed by the user's wallet, proving they authorized the operation.

Rate Limiting

  • 100 requests per minute per IP address
  • Returns HTTP 429 when exceeded

Common Prerequisites

Before submitting any intent:

1. Token Approval

The user must approve the MultiHopRouter contract to spend their input tokens:

javascript
import { erc20Abi } from 'viem';

const MULTIHOP_ROUTER = '0x662F15226f5b6Bf8aA10512374Af3115412C04bA';

const hash = await walletClient.writeContract({
  address: tokenAddress,
  abi: erc20Abi,
  functionName: 'approve',
  args: [MULTIHOP_ROUTER, amountIn], // or MaxUint256 for unlimited
});
await publicClient.waitForTransactionReceipt({ hash });

2. Read Nonce

Each intent type has its own nonce counter on the contract. Read the current nonce before signing:

javascript
// For swaps
const nonce = await publicClient.readContract({
  address: MULTIHOP_ROUTER,
  abi: routerAbi,
  functionName: 'swapNonces',
  args: [userAddress],
});

// For single joins
const nonce = await publicClient.readContract({
  address: MULTIHOP_ROUTER,
  abi: routerAbi,
  functionName: 'joinNonces',
  args: [userAddress],
});

// For proportional joins
const nonce = await publicClient.readContract({
  address: MULTIHOP_ROUTER,
  abi: routerAbi,
  functionName: 'joinProportionalNonces',
  args: [userAddress],
});

// For exits
const nonce = await publicClient.readContract({
  address: MULTIHOP_ROUTER,
  abi: routerAbi,
  functionName: 'exitProportionalNonces',
  args: [userAddress],
});

3. EIP-712 Domain

All intent types share this domain:

javascript
const domain = {
  name: 'MultiHopRouter',
  version: '1',
  chainId: 8453, // Base mainnet (84532 for testnet)
  verifyingContract: '0x662F15226f5b6Bf8aA10512374Af3115412C04bA',
};

Error Format

json
{
  "success": false,
  "status": "error",
  "message": "Verification failed: nonce mismatch: expected 1, got 0 (hint: use 1 for your next intent)"
}
CodeCommon Causes
400Invalid signature, nonce mismatch, expired deadline, bad path
429Rate limit exceeded
503Service unhealthy