In this guide, you will create your first Omni Account and make a cross-chain swap in 5 minutes.

Prerequisites

You don’t need an API key to get started. For production use, you’ll need one. Reach out to us.
You will need some testnet funds. To get testnet ETH, you can use a faucet from Quicknode or Alchemy. To get testnet USDC, use Circle Faucet. Install the Rhinestone SDK:
npm install viem @rhinestone/sdk@alpha

Creating a Wallet

Let’s create a smart account with a single owner:
import { createRhinestoneAccount } from '@rhinestone/sdk'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
import { baseSepolia, arbitrumSepolia } from 'viem/chains'
import {
  createPublicClient,
  createWalletClient,
  encodeFunctionData,
  erc20Abi,
  type Hex,
  http,
  parseEther,
} from 'viem'

const fundingPrivateKey = process.env.FUNDING_PRIVATE_KEY
if (!fundingPrivateKey) {
  throw new Error('FUNDING_PRIVATE_KEY is not set')
}

const sourceChain = baseSepolia
const targetChain = arbitrumSepolia

// You can use an existing PK here
const privateKey = generatePrivateKey()
console.log(`Owner private key: ${privateKey}`)
const account = privateKeyToAccount(privateKey)

const rhinestoneAccount = await createRhinestoneAccount({
  owners: {
    type: 'ecdsa',
    accounts: [account],
  },
})
const address = rhinestoneAccount.getAddress()
console.log(`Smart account address: ${address}`)

Funding the Account

We will send some ETH from the funding account to the created smart account. The Orchestrator will use some of that ETH to deploy the account on the target chain, as well as to convert it to USDC for a transfer transaction.
const publicClient = createPublicClient({
  chain: sourceChain,
  transport: http(),
})
const fundingAccount = privateKeyToAccount(fundingPrivateKey as Hex)
const fundingClient = createWalletClient({
  account: fundingAccount,
  chain: sourceChain,
  transport: http(),
})

const txHash = await fundingClient.sendTransaction({
  to: address,
  value: parseEther('0.001'),
})
await publicClient.waitForTransactionReceipt({ hash: txHash })

Sending a Cross-chain Transaction

Finally, let’s make a cross-chain token transfer:
const usdcAmount = 1n

const transaction = await rhinestoneAccount.sendTransaction({
  sourceChains: [sourceChain],
  targetChain,
  calls: [
    {
      to: 'USDC',
      value: 0n,
      data: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transfer',
        args: ['0xd8da6bf26964af9d7eed9e03e53415d37aa96045', usdcAmount],
      }),
    },
  ],
  tokenRequests: [
    {
      address: 'USDC',
      amount: usdcAmount,
    },
  ],
})
console.log('Transaction', transaction)

const transactionResult = await rhinestoneAccount.waitForExecution(transaction)
console.log('Result', transactionResult)
After running that, you will get a smart account deployed on both Base Sepolia and Arbitrum Sepolia, and make a cross-chain USDC transfer. Note that you don’t need to manage the gas tokens or do the ETH → USDC swap when making a transfer. The Orchestrator will handle that for you!

Next Steps

To get an in-depth overview of the Omni Account features, check out the Send Omnichain Transactions and follow our Chain Abstraction guides. To learn more about using Omni Accounts with smart EOAs, check out our EIP-7702 guide. To learn more about using validator modules, check out our Multisig, Passkeys, and Smart Sessions guides.