Skip to main content
Create a smart account and send a crosschain transaction. By the end you’ll have an account deployed on two chains and a USDC transfer that crossed between them, funded entirely with ETH.

Prerequisites

You’ll need a funding account with some testnet ETH on Base Sepolia. Get testnet ETH from QuickNode or Alchemy. Install the SDK:
npm install viem @rhinestone/sdk@beta
pnpm add viem @rhinestone/sdk@beta
bun install viem @rhinestone/sdk@beta
1

Create an account

Create a smart account with a single ECDSA owner:
import { RhinestoneSDK } from '@rhinestone/sdk'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
import { baseSepolia, arbitrumSepolia } from 'viem/chains'
import {
  createPublicClient,
  createWalletClient,
  encodeFunctionData,
  erc20Abi,
  type Hex,
  http,
  parseEther,
  parseUnits,
} 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 rhinestone = new RhinestoneSDK({
  apiKey: process.env.RHINESTONE_API_KEY,
})
const rhinestoneAccount = await rhinestone.createAccount({
  owners: {
    type: 'ecdsa',
    accounts: [account],
  },
})
const address = rhinestoneAccount.getAddress()
console.log(`Smart account address: ${address}`)
You’ll see a deterministic address printed:
Smart account address: 0x...
Nothing is onchain yet: the account is deployed lazily on first use, on each chain it touches.
2

Fund the account

Send ETH to the smart account on the source chain. This is the only token you fund:
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 })
3

Send a crosschain transaction

Transfer USDC on the target chain, sourced from the ETH you funded on the source chain:
const usdcAmount = parseUnits('0.1', 6)

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

const transactionResult = await rhinestoneAccount.waitForExecution(transaction)
console.log('Result', transactionResult)
submitTransaction returns an intent handle; waitForExecution polls until every chain’s operation reaches a terminal state:
Transaction {
  type: 'intent',
  id: '0x9c…',
  traceId: '…',
  sourceChains: [ 84532 ],
  targetChain: 421614
}
Result {
  status: 'COMPLETED',
  accountAddress: '0x5fA3…42c1',
  operations: [
    { chain: 84532, status: 'COMPLETED', txHash: '0x…', timestamp: 1750000000 },
    { chain: 421614, status: 'COMPLETED', txHash: '0x…', timestamp: 1750000002 }
  ]
}
Your ETH on Base Sepolia landed as USDC on Arbitrum Sepolia in a single atomic operation: no bridging, swapping, or gas tokens to manage.
Building a browser app? Use the Reown + Rhinestone example to get wallet connection working with MetaMask or any WalletConnect-compatible wallet.

Next steps

Sponsor fees

Cover gas, bridging, and swap fees for your users across any chain.

Smart account setup

Configure signers: passkeys, embedded wallets, multisig, and more.

Smart Sessions

Add session keys for one-click UX and automated transactions.