Skip to content

Use a Unified Multichain Balance

Today, users are interacting with multiple blockchains and it's quite often that their balances are spread across all of those chains.

Supertransactions provide apps with the ability to unify the user balances across all of those chains and access them as if they're on the target chain.

Prerequisites

Install required dependencies

npm install @biconomy/abstractjs viem

Initialize a Multichain Account

AbstractJS has a utility type called MultichainAccount which simplifies the Deployments and interactions for Smart Accounts deployed across multiple blockchains.

To learn more, visit: Learning About Multichain Smart Accounts

const privateKey = '0xYour private key'
const eoa = privateKeyToAccount(privateKey)
 
const mcNexus = await toMultichainNexusAccount({
  chains: [optimism, base, polygon, arbitrum],
  signer: eoa
})

Load a Multichain Contract for the Token

One of the main concepts of AbstractJS is the ability to initialize MultichainContract instances. This is an object that combines the deployments of a Smart Contract across multiple chains.

This is an example of instantiating a MultichainContract for an AAVE Pool which is deployed on multiple chains

const mcAavePool = getMultichainContract({
  abi: aaveV3PoolAbi,
  deployments: [
    ['0xAddressOnOp', optimism.id],
    ['0xAddressOnBase', base.id],
    // The rest of the chains
  ]
})
Multichain Token Contract

For an ERC20 token, we can instantiate the MultichainContract instance with an erc20Abi (exposed by viem) as the ABI!

const mcUSDC = getMultichainContract({
  abi: erc20Abi,
  deployments: [
    // USDC Deployments on Chains
  ]
})

To learn more, visit: Learning About Multichain Contracts

Accessing Common Tokens

To save you the trouble of finding token addresses for every chain and every token you use, AbstractJS exports common tokens.

import { mcUSDC } from "./utils/tokens";
 
// Use the exported token multichain contract
console.log(mcUSDC.addressOn(optimism.id))

Reading a Unified ERC20 Balance

Get the unified token balance across all chains:

const balance = await getUnifiedERC20Balance({
  multichainAccount: mcNexus,
  multichainERC20: mcUSDC
})
 
balance.balance // Total balance across all chains
balance.breakdown // Breakdown of balances across chains
balance.decimals // Amount of decimals of the token

Requiring a Balance on a Target Chain

Beyond just reading a Unified Balance, AbstractJS comes with pre-build utilities for encoding Supertransaction steps which will make sure the user has a certain amount of some token on some target chain before executing the desired action.

Since these instructions will be a part of the same Supertransaction, the user will sign just once and all of their balances on all chains will be pulled.

Encoding the required steps

To make sure that the user has uniBalance amount of USDC on OP Mainnet, you would write this code:

const steps = await requireErc20Balance({
  account: mcNexus,
  amount: uniBalance,
  chain: optimism,
  token: mcUSDC
})

The requireErc20Balance function will do multiple things:

Check target chain balance

Check if the user has uniBalance amount of tokens on OP Mainnet

Attempt bridging and solving

If not, try to use solvers and bridges to get the uniBalance amount of tokens to OP Mainnet

Throw error if user doesn't have enough

If the user doesn't have enough tokens across all chains, throw an Error

Encode solving and bridging steps

If the user has enough tokens - encode the steps required to get the tokens to OP Mainnet as Supertransaction instructions

Building a Supertransaction

Then, when you are building your Supertransaction, simply include these steps:

const superTx: SuperTransaction = {
  instructions: [
    // Steps calculated from the requireErc20Balance function
    ...steps, 
    mcUSDC.on(optimism.id).transfer({
      args: [recipient, uniBalance],
      gasLimit: 100_000n
    })
  ],
  feeToken: toFeeToken({
    chainId: optimism.id,
    token: mcUSDC
  })
}