Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks through the full intent flow for EOA users: get a quote, fulfill token requirements, sign, and submit.
Using the Rhinestone SDK? The wallet quickstart already covers crosschain intents. Come back here when you need API-specific configuration.Using an existing smart account (non-Rhinestone SDK)? You’ll need to install the Intent Executor on each account before using Warp, then follow the smart account signing guide for the signing step.

Prerequisites

Steps

1

Get a quote

Submit a meta intent to the /quotes endpoint. Specify your destination chain, the token and amount you want on that chain, and your account:
const BASE_URL = "https://v1.orchestrator.rhinestone.dev";
const API_KEY = "YOUR_RHINESTONE_API_KEY";
const EOA_ADDRESS = "0xYourEOAAddress";

const headers = {
  "Content-Type": "application/json",
  "x-api-key": API_KEY,
  "x-api-version": "2026-04.blanc",
};

const res = await fetch(`${BASE_URL}/quotes`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    account: {
      address: EOA_ADDRESS,
      accountType: "EOA",
    },
    destinationChainId: "eip155:8453", // Base
    tokenRequests: [
      {
        tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
        amount: "5000000", // 5 USDC (6 decimals)
      },
    ],
  }),
});

const { routes } = await res.json();
const route = routes[0];
const { intentId, signData, tokenRequirements } = route;
The response is a server-ranked routes array. Use routes[0] unless you have your own ranking. Each route carries:
  • intentId: server-stored handle, used to submit
  • cost: input/output amounts and fee breakdown
  • signData: EIP-712 typed data to sign
  • tokenRequirements: approvals or wrapping the user must complete before signing
2

Fulfill token requirements

Before signing, the user must fulfill any tokenRequirements returned in the quote. Keys are CAIP-2 chain ids (eip155:8453):ERC-20 approvals — approve tokens to the Permit2 contract:
import { maxUint256 } from "viem";

for (const [chainId, tokens] of Object.entries(tokenRequirements)) {
  for (const [tokenAddress, requirement] of Object.entries(tokens)) {
    if (requirement.type === "approval") {
      await walletClient.writeContract({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: "approve",
        args: [requirement.spender, maxUint256],
      });
    }
  }
}
ETH wrapping — wrap native ETH to WETH:
for (const [chainId, tokens] of Object.entries(tokenRequirements)) {
  for (const [tokenAddress, requirement] of Object.entries(tokens)) {
    if (requirement.type === "wrap") {
      await walletClient.writeContract({
        address: WETH_ADDRESS,
        abi: wethAbi,
        functionName: "deposit",
        value: requirement.amount,
      });
    }
  }
}
Use max approvals to the Permit2 contract. This is the only contract you ever approve — future intents won’t need a new approval.
3

Sign the intent

Forward signData.origin[] and signData.destination directly to signTypedData. One signature per source chain, plus the destination signature:
const originSignatures = await Promise.all(
  signData.origin.map((typedData) =>
    walletClient.signTypedData(typedData),
  ),
);
const destinationSignature = await walletClient.signTypedData(
  signData.destination,
);

Signing guide

Smart account signing and validator wrapping.
4

Submit the intent

Post the signed intent to /intents:
const submitRes = await fetch(`${BASE_URL}/intents`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    intentId,
    signatures: {
      origin: originSignatures,
      destination: destinationSignature,
    },
  }),
});

const { intentId: submittedId } = await submitRes.json();
If submit returns 404, the quote TTL elapsed — re-quote and re-sign.
5

Poll for completion

Track execution status using the intentId:
async function pollStatus(intentId: string) {
  while (true) {
    const res = await fetch(`${BASE_URL}/intents/${intentId}`, { headers });
    const { status } = await res.json();

    if (["COMPLETED", "FAILED", "EXPIRED"].includes(status)) {
      console.log("Final status:", status);
      return status;
    }

    await new Promise((resolve) => setTimeout(resolve, 2000));
  }
}

await pollStatus(submittedId);
Typical execution time is under 2 seconds. See Tracking intents for the full lifecycle.

Next steps

Getting a Quote

Advanced quote options: sponsorship, source chain filtering, destination executions.

Token Requirements

Full details on approvals and ETH wrapping.

Error Handling

Common Orchestrator errors and how to fix them.