Adapters - Relay

Documentation Index

Fetch the complete documentation index at: /llms.txt

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

What is an adapter anyway?

An adapter is a function that takes parameters and returns an object that adheres to this interface. Let’s review the interface in depth:

Property Description Required
vmType A string representing a supported vmType (evm``svm``bvm``tvm``lvm``tonvm) ✅
getChainId An async function that returns a chain id ✅
handleSignMessageStep An async function that given a signature step item and a step generates a signature ✅
handleSendTransactionStep An async function that given a chain id, a transaction step item and a step returns a transaction hash ✅
handleConfirmTransactionStep An async function that given a transaction hash, a chain id, an onReplaced function and an onCancelled function returns a promise with either an evm receipt or an svm receipt ✅
address An async function that returns the currently connected to address, an address that can sign messages and submit transactions ✅
switchChain An async function that given a chain id switches to that chain, either by prompting the user or automatically switching chains ✅
transport An optional transport to use when making rpc calls. ❌
getBalance An optional method to override the default balance fetching logic for selected tokens in the ui kit. ❌
supportsAtomicBatch An optional async function that takes a chain ID and returns whether the wallet supports EIP-5792’s atomic batch capability. EVM wallets only. ❌
handleBatchTransactionStep An optional async function that takes a chain ID and an array of transaction step items, returning a call bundle identifier for batch processing. Only available for EVM wallets that support atomic batching. ❌
isEOA An optional boolean that indicates if the wallet is an EOA (Externally Owned Account). This is used to determine if the wallet is an EOA or a contract account. ❌

What adapters are available out of the box?

The following adapters are officially maintained and developed by the Relay team:

How can I use an adapter?

You can either make your own adapter, as long as it adheres to the interface above or you can use one of the officials adapters developed and maintained by the Relay team. All of our sdk methods handle a viem wallet or adapted wallet. The viem wallet adapter is used by default when a viem wallet is passed in for convenience. Refer below for implementation details:

Solana

import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk";
import { adaptSolanaWallet } from '@relayprotocol/relay-solana-wallet-adapter'
import { Connection, Keypair, clusterApiUrl, SystemProgram, Transaction } from '@solana/web3.js';
...

//In this example we are loading a keypair as the wallet, but if you have a connector like dynamic you can just fetch the connection from that library
const wallet = Keypair.generate();
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
const walletAddress = wallet.publicKey.toString()

const adaptedWallet = adaptSolanaWallet(
  walletAddress,
  792703809, // Chain ID that Relay uses to identify Solana
  connection, // The Solana web3.js Connection instance for interacting with the network
  connection.sendTransaction // Function to sign and send a transaction, returning a promise with a base58 encoded transaction signature
)

const options = ... // define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({steps, fees, breakdown, currentStep, currentStepItem, txHashes, details}) => {
        // custom handling
  },
  ...
})

Viem

import { getClient, Execute, getQuote, adaptViemWallet } from "@relayprotocol/relay-sdk";
import { createWalletClient } from 'wagmi'
import { mainnet } from 'viem/chains'

...

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

const options = ... // define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptViemWallet(walletClient),
  onProgress: ({steps, fees, breakdown, currentStep, currentStepItem, txHashes, details}) => {
        // custom handling
  },
  ...
})

Bitcoin

import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk";
import { adaptBitcoinWallet } from '@relayprotocol/relay-bitcoin-wallet-adapter'
import { createWalletClient } from 'wagmi'
import { mainnet } from 'viem/chains'

...

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

const options = ... //define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

const adaptedWallet = adaptBitcoinWallet(
  primaryWallet.address,
  async (_address, _psbt, dynamicParams) => {
    try {
      // Request the wallet to sign the PSBT (this is using dynamic but you could use whatever framework you want)
      const response = await primaryWallet.signPsbt(dynamicParams)
      if (!response) {
        throw 'Missing psbt response'
      }
      return response.signedPsbt
    } catch (e) {
      throw e
    }
  }
)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({steps, fees, breakdown, currentStep, currentStepItem, txHashes, details}) => {
        //custom handling
  },
  ...
})

Ethers

import { getClient } from '@relayprotocol/relay-sdk'
import { adaptEthersSigner } from '@relayprotocol/relay-ethers-wallet-adapter'
import { useSigner } from 'wagmi'

...

const { data: signer } = useSigner()
const adaptedWallet = adaptEthersSigner(signer)

const options = ... //define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

#### Tron

```javascript
import { TronWallet } from 'tronweb'
import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk"
import { adaptTronWallet } from '@relayprotocol/relay-tron-wallet-adapter'

// Create a TronWeb instance, we recommend using a provider like dynamiclabs if using this on the frontend
// Note: you should never share your private key with anyone or use on the frontend
const tronWeb = new TronWeb({fullHost: 'xxx', privateKey: 'privateKey'});
const walletAddress = tronWeb.defaultAddress.base58
if (!tronWeb) {
  throw 'Unable to setup Tron wallet'
}

const adaptedWallet = adaptTronWallet(
  walletAddress,
  tronWeb
)
const options = ... //define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

#### Lighter

```javascript
import { getClient, getQuote } from '@relayprotocol/relay-sdk'
import { adaptLighterWallet } from '@relayprotocol/relay-lighter-wallet-adapter'
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

// Lighter deposits are authorized by an L1 (EVM) signature, so the adapter
// only needs the user's connected EVM address and a signMessage callback.
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum!)
})

const [address] = await walletClient.requestAddresses()

const adaptedWallet = adaptLighterWallet({
  l1Address: address,
  signL1Message: (message) =>
    walletClient.signMessage({ account: address, message })
})

const options = ... // define this based on getQuote options (destinationChainId: 3586256, recipient: <Lighter account index>)

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({ steps, fees, breakdown, currentStep, currentStepItem, txHashes, details }) => {
    // custom handling
  }
})

TON

import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk"
import { adaptTonWallet } from '@relayprotocol/relay-ton-wallet-adapter'

// The user's key lives in their TON wallet, so the adapter never signs. You
// provide a sendTransaction callback that forwards the TonConnect-style request
// to your wallet provider (e.g. Dynamic's TON connector or tonConnectUI), signs
// + broadcasts it, and returns the result. The adapter accepts a signed
// external-message { boc } (what most TonConnect wallets return) or a
// { transactionHash }. The confirmation RPC is read from the TON chain's
// httpRpcUrl in your Relay client config — there is no adapter-level endpoint.
const adaptedWallet = adaptTonWallet(walletAddress, async (request) => {
  const { boc } = await wallet.sendTransaction(request)
  return { boc }
})

const options = ... // define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet: adaptedWallet,
  onProgress: ({ steps, fees, breakdown, currentStep, currentStepItem, txHashes, details }) => {
    // custom handling
  },
  ...
})

Viem adapter options

adaptViemWallet accepts an optional second argument for adapter-level configuration:

Property Description
disableCapabilitiesCheck Skip wallet.getCapabilities calls used for EIP-5792 atomic-batch detection and smart-wallet detection. Set this for wallets with a broken getCapabilities implementation that hangs or never resolves. When true, execution falls back to sequential transactions.
import { adaptViemWallet } from "@relayprotocol/relay-sdk";

const adaptedWallet = adaptViemWallet(walletClient, {
  disableCapabilitiesCheck: true,
});

The same flag is available directly on getQuote, execute, and claimAppFees when you pass a raw viem WalletClient — the SDK forwards it to adaptViemWallet internally.