Skip to main content
Fast Fill is a feature that allows you to accelerate the destination fill of a cross-chain request. This is useful if you want to speed up the completion of a bridge or swap operation before it’s landed onchain.

Requirements

Before you can start fast filling requests, you need:
  1. An API Key — Required to authenticate your fast fill requests. Request an api key.
  2. Enterprise Partnership — Reach out to the Relay team to become an Enterprise Partner. Learn more about the Enterprise Partner Program.
  3. A Linked Funding Address — A wallet address must be linked to your API key. This is the address that will fund your app balance. Reach out to the Relay team to link your wallet address.
  4. Sufficient App Balance — Your app balance must have enough funds to cover fast fills. Fast fills draw from your app balance and restore it once transactions land onchain. App balance is also used for fee sponsorship.
Once you’re set up, you can begin fast filling requests.

How to use it?

To Fast Fill a request you simply need to call the Fast Fill API with the request ID of the request you want to fast fill after you’ve submitted it onchain but before it finalizes. The request will be instantly filled and the fill amount will be deducted from your app balance. Once the transaction lands onchain and Relay indexes it, the funds are restored to your app balance.

Example

This example demonstrates the full fast fill flow: getting a quote, submitting the transaction on-chain, calling the fast fill API, and monitoring the status.
import { getClient, createClient } from "@relayprotocol/relay-sdk";
import { createWalletClient, createPublicClient, http } from "viem";
import { base } from "viem/chains";

// 1. Setup Wallet - Initialize your wallet using your preferred method
const account = {}; // Your wallet account (e.g., privateKeyToAccount, or injected wallet)
const wallet = createWalletClient({
  account,
  chain: base,
  transport: http(),
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(),
});

// 2. Initialize the Relay client
createClient({
  baseApiUrl: "https://api.relay.link",
  source: "YOUR_APP_NAME",
});

// 3. Get a quote using the SDK
const quote = await getClient().actions.getQuote({
  chainId: 8453, // Base
  toChainId: 42161, // Arbitrum
  currency: "0x0000000000000000000000000000000000000000", // ETH
  toCurrency: "0x0000000000000000000000000000000000000000", // ETH
  amount: "100000000000000", // 0.0001 ETH
  tradeType: "EXACT_INPUT",
  wallet,
});

const requestId = quote.steps[0].requestId;
console.log(`Request ID: ${requestId}`);

// 4. Execute the quote and fast fill once the transaction is submitted
let txHash: string;

await getClient().actions.execute({
  quote,
  wallet,
  onProgress: async ({ currentStep, currentStepItem, txHashes }) => {
    // Once transaction is submitted, trigger fast fill immediately
    if (txHashes && txHashes.length > 0 && !txHash) {
      txHash = txHashes[0];
      console.log(`Transaction submitted: ${txHash}`);

      // 5. Call Fast Fill immediately after submitting (before waiting for confirmation)
      const fastFillResult = await getClient().actions.fastFill({
        requestId,
      });
      console.log("Fast fill initiated:", fastFillResult);
    }

    // Monitor progress
    if (currentStep && currentStepItem) {
      console.log(`Step: ${currentStep.action}, Status: ${currentStepItem.status}`);
    }
  },
});

console.log("Bridge completed successfully!");
You can use the maxFillAmountUsd parameter to set a maximum USD value for a fast fill request. If the request’s value exceeds this limit, the fast fill will not be executed. This is useful for managing risk and controlling exposure, especially when dealing with volatile assets or large transaction volumes.

Funding Your App Balance

There are two ways to deposit funds to your app balance:

Option 1: Use the Relay App UI

The simplest way to deposit funds is through the Relay App Balance UI. This provides a user-friendly interface for managing your balance.

Option 2: Direct On-Chain Deposit

You can programmatically deposit to your app balance by sending a transaction on Base to the Relay solver. This method involves appending specific calldata in place of the request ID to identify the deposit.

How It Works

When you transfer funds to the solver using specific calldata, Relay treats it as a deposit to your app balance. This method uses a fixed 12-character prefix and suffix (012345abcdef) with the middle portion specifying which address to credit: Calldata Format:
0x[prefix][address-to-credit][suffix]
  • Prefix: 012345abcdef
  • Address to Credit: The wallet address to deposit funds for (use 0000000000000000000000000000000000000000 for msg.sender)
  • Suffix: 012345abcdef

Examples

CalldataBehavior
0x012345abcdef0000000000000000000000000000000000000000012345abcdefCredits msg.sender
0x012345abcdefd5c0d17ccb9071d27a4f7ed8255f59989b9aee0d012345abcdefCredits 0xd5c0d17ccb9071d27a4f7ed8255f59989b9aee0d
The ability to specify a different address is useful for building auto-topup systems or having more granular control over which accounts receive deposits.

Solver Address

Deposits should be sent to the Relay solver on Base:
NetworkAddress
Base0xf70da97812cb96acdf810712aa562db8dfa3dbef

TypeScript Example

import { erc20Abi, encodeFunctionData, createWalletClient } from 'viem'
import { base } from 'viem/chains'

const BASE_USDC_ADDRESS = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' as const
const RELAY_BASE_SOLVER_ADDRESS = '0xf70da97812cb96acdf810712aa562db8dfa3dbef' as const
const REQUEST_ID_PREFIX = '012345abcdef'

const walletClient = createWalletClient({
  chain: base,
  transport: custom(window.ethereum!),
})

const [userAddress] = await walletClient.getAddresses()

const amountInWei = '100000000' // 100 USDC

// Encode the transfer function call
const transferData = encodeFunctionData({
  abi: erc20Abi,
  functionName: 'transfer',
  args: [RELAY_BASE_SOLVER_ADDRESS, amountInWei]
})

// Add custom calldata to credit the wallet address
const customCalldata =
  `${REQUEST_ID_PREFIX}${userAddress.slice(2)}${REQUEST_ID_PREFIX}` as `0x${string}`

const hash = await walletClient.sendTransaction({
  to: BASE_USDC_ADDRESS,
  data: `${transferData}${customCalldata}`,
  account: userAddress,
  chain: base
})

Checking Your App Balance

Verify your available balance using the Get App Fee Balances API:
curl --location 'https://api.relay.link/app-fees/{your-wallet-address}/balances'

Caveats

  • Slippage may lead to a surplus or shortage in your app balance. If you have a good estimate of the final amount you can use the solverInputCurrencyAmount parameter to specify the exact amount of input currency you want to use for the fill, thus minimizing slippage.
  • We recommend protecting your API key on the backend by not exposing it to the client.