Overview

Intents are expressions of desired outcomes that sophisticated third-party actors fulfill. These third-party actors are often referred to as solvers or relayers. Importantly, intents are chain-agnostic. With the right settlement infrastructure and relayer marketplace, intents enable fast, cheap, and generalized executions to happen on any chain. Rhinestone’s intent system is a modular engine that aggregates settlement layers through one unified relayer market. It utilizes resource locks for asynchronous execution and blazing-fast speed.
The intent system uses the core sendTransaction API. Some advanced features shown in examples may require additional implementation based on your account setup and the current intent system capabilities.
What makes intents powerful:
  • Express desired outcomes, not execution steps
  • Unified relayer market aggregates settlement layers
  • Atomic and deterministic cross-chain execution
  • Single signature for multi-chain, multi-token operations

Intent Fundamentals

Intent Structure

Every intent is composed of Elements, where each Element represents a chain-specific commitment. An Element defines the settlement layer, chain ID, and locked token information, along with a Mandate that specifies the recipient, output tokens, expiry, and the destination chain.
const intent = {
  sourceChains: [arbitrum, base], // Origin chains with available tokens
  targetChain: ethereum,          // Destination chain for execution
  calls: [...],                   // Destination chain executions
  tokenRequests: [...],           // Required output tokens
  // Optional: origin chain executions (pre-claim operations)
}
Key concepts:
  • Elements: Chain-specific commitments with settlement layer info
  • Mandate: Defines recipient, output tokens, and destination execution
  • Origin executions: Pre-claim operations before settlement
  • Destination executions: Operations executed after token delivery

Basic Cross-Chain Transfer

The simplest intent - move tokens from one chain to another:
const transferIntent = await rhinestoneAccount.sendTransaction({
  sourceChains: [arbitrum],
  targetChain: base,
  calls: [
    {
      to: baseUSDC,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [recipient, amount],
      }),
    },
  ],
  tokenRequests: [
    {
      address: baseUSDC,
      amount: parseUnits('100', 6), // 100 USDC
    },
  ],
})

Single-Chain Transaction

Execute on one chain using local tokens:
const singleChainTx = await rhinestoneAccount.sendTransaction({
  chain: ethereum,
  calls: [
    {
      to: tokenAddress,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: [recipient, parseUnits('1', 18)],
      }),
    },
  ],
})

Monitor Execution

const result = await rhinestoneAccount.waitForExecution(transferIntent)
console.log('Status:', result.status)

Token Requests

tokenRequests is a list of token assets and their amounts that are required on the target chain to make the transaction. It tells the solvers to ensure those assets are present before executing the transaction calls. If you don’t need any assets on the target chain, you can omit this.
Solvers will supply all assets specified in the tokenRequests. If the user already has the required assets, don’t add them into tokenRequests.

Gas Limit

You can override the default gas limit for the target chain execution with gasLimit. Doing this will make the intent better priced, because we can more accurately calculate the fee that a solver needs to be reimbursed with for paying the gas. If this is not provided, we calculate using a gas limit of 1_000_000.
const transaction = await rhinestoneAccount.sendTransaction({
  sourceChains: [baseSepolia],
  targetChain: arbitrumSepolia,
  calls: [
    // …
  ],
  tokenRequests: [
    // …
  ],
  gasLimit: 200_000n,
})

Source Chain

Providing the source chain deploys the account on that chain, as well as uses the funds on that chain to fulfill the intent. If you already have an account deployed on one or more source chains, you can omit the sourceChain. In that case, the orchestrator will use the best chain(s) to source funds.