Chain Abstraction
ZeroDev is the first smart account solution to support chain abstraction.
With ZeroDev, you can turn a token balance into a "chain-abstracted balance" (CAB). Instead of thinking about "USDC on Ethereum" vs "USDC on Arbitrum," your user can just look at their "USDC balance" as a single balance that can be spent on any chain.
Demo
Try this demo of chain abstraction.
Features
One unified balance
As aforementioned, CAB (chain-abstracted balance) is a single balance that can be spent on any chain.
For example, if you deposit 100 USDC to a ZeroDev account on Base, then deposit 200 USDC to the account on Polygon, the account will now hold 300 "chain-abstracted USDC" that can be spent on any chain.
No bridging latency
Amazingly, CAB can be spent at the speed of the destination chain -- just like a regular transaction. This is unlike a traditional bridge where the user has to wait for confirmations on both the source chain and the destination chain.
Spending as any tokens
CAB can be spent as any supported tokens; ZeroDev automatically handles the swapping. For example, if you have 500 USDC in CAB but you need to spend 500 USDT, you can do that.
Currently, we support swapping USDC to USDT/DAI/WETH/WBTC. Contact us if you are looking to support any specific tokens.
Code example
Here are complete code examples for using CAB. To run the example you need to set the environment variable CAB_PAYMASTER_URL
which you can find here.
Usage
Installation
npm i @zerodev/cab
Setting up the Kernel account
Skip to the next section if your ZeroDev app is already live. This section is for developers who can still change how they set up their Kernel accounts.
For the best experience with CAB, we recommend setting up your Kernel account with a multi-chain validator, which is a validator that can sign messages for multiple chains in one signature. This is useful for CAB because CAB needs to be enabled for each chain, so with a multi-chain validator you can enable CAB with just one signature, whereas with a regular validator you'd need to sign once per chain.
To set up a multi-chain ECDSA validator (code example):
import { toMultiChainECDSAValidator } from "@zerodev/multi-chain-validator"
const ecdsaValidator = await toMultiChainECDSAValidator(publicClient, {
signer,
entryPoint,
kernelVersion,
})
To set up a multi-chain passkeys validator, check out this example.
Once you have created a validator, you can set up a Kernel client with this validator as usual.
Setting up the CAB client
To use CAB, wrap the kernel client into a CAB client:
import { createKernelCABClient } from "@zerodev/cab"
const cabClient = createKernelCABClient(kernelClient, {
transport: http(CAB_PAYMASTER_URL),
})
For testing, you can use https://cab-paymaster-service.onrender.com/paymaster/api
as the CAB_PAYMASTER_URL
. You need to contact us to obtain a CAB paymaster URL for production.
Enabling chain-abstracted balance
Before using CAB, you need to enable CAB for the smart account, by specifying a list of tokens you wish to support. Currently, CAB supports USDC only. Please let us know if there are specific tokens you wish us to support.
await cabClient.enableCAB({
tokens: [{
name: "USDC",
// networks: [arbitrum.id, base.id],
}]
})
You can optionally specify the list of networks to enable CAB for. If you don't specify it, CAB is enabled for all supported networks.
If you are not using a multi-chain validator, you need to enableCAB for each network, one at a time:
await cabClient.enableCAB({
tokens: [{
name: "USDC",
networks: [arbitrum.id],
}]
})
Spending chain-abstracted balance
There are three steps to spending CAB:
- Construct the calls
- Prepare the CAB UserOp and token info
- Send the UserOp
First, start by constructing the calls. You can include multiple calls if you'd like to send a batch.
// In this example, we are performing a USDC transfer
const calls = [
{
to: usdcContractAddress,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [account.address, BigInt(1000)]
}),
value: BigInt(0),
}
]
Then, prepare the CAB UserOp by specifying the calls and repay tokens. Note that the call can spend a different token than the repay token. Currently the repay token can only be USDC, but the call can spend USDC/USDT/DAI/WETH/WBTC. If the repay token and the spent token are different, the ZeroDev SDK automatically performs the swapping.
const { userOperation, repayTokensInfo, sponsorTokensInfo } =
await cabClient.prepareUserOperationRequestCAB({
calls,
repayTokens: ['USDC'],
})
Here you have three pieces of data which you can display to users for them to confirm the transaction, if you wish:
userOperation
is the full UserOp that will be sent.repayTokensInfo
is information for the repay tokens (the tokens the user is paying solvers)sponsorTokensInfo
is information for the sponsor tokens (the tokens the user is spending, aka the tokens paid by solvers)
Finally, send the CAB UserOp:
const userOpHash = await cabClient.sendUserOperationCAB({
userOperation,
})
Reading chain-abstracted balance
To read the CAB balance for an account:
const cabBalance = await cabClient.getCabBalance({
address: account.address,
token: 'USDC',
})