Account Abstraction for Web
Particle Network natively supports and facilitates end-to-end utilization of ERC-4337 account abstraction. This is primarily done through the account abstraction SDK, capable of constructing, sponsoring, and sending UserOperations, alongside deploying smart accounts, retrieving fee quotes, and other key functions. At the center of Particle Network's Modular Smart Wallet-as-a-Service is the AA SDK, which fully supports web applications.
Repository
The Particle Network aa-sdk
has an open-source GitHub repository providing full transparency of the underlying architecture. It may be worthwhile to look at this repository to contextualize the information below, making it easier to digest.
Getting Started
Configuring and initializing the Particle Network AA SDK is simple, although there are two key steps to consider beforehand: installing @particle-network/aa
with either Yarn or npm and retrieving required core values from the Particle dashboard.
Installation
To begin with installation, you'll need to choose either Yarn or npm, then run the command detailed below within your shell:
yarn add @particle-network/aa
// Or
npm install @particle-network/aa
Setting up the Particle dashboard
You'll also need required values from the Particle dashboard: your projectId
, clientKey
, and appId
. Each of these values will be used in the initial configuration/initialization of the SDK and directly link your instance of the SDK with the Particle dashboard. Retrieving these values can be done by following the process below:
- Sign up/log in to the Particle dashboard.
- Create a new project or enter an existing one.
- Create a new web application, or skip this step if you already have one.
- Retrieve the project ID (
projectId
), the client key (clientKey
), and the application ID (appId
).
Important details before initialization
Before initializing the SDK, there are a few key points to keep in mind, specifically regarding the utilization of Paymasters (to sponsor gas fees, pay for gas in ERC-20 tokens, etc.)
- All Testnets automatically have the Verifying Particle Network Omnichain Paymaster enabled. Transactions that request it will automatically be sponsored and thus gasless.
- On the occasion that you'd like to use the Particle Network Omnichain Paymaster for Mainnets, you'll need to deposit USDT on either Ethereum or BNB Chain within the Particle dashboard. This USDT will then automatically be converted as needed into the native token of the network you're requesting (and qualifying for) sponsorship on.
- Alternatively, if you'd like to instead use Biconomy's Verifying Paymaster, you can head over to the Biconomy dashboard, create a new Paymaster, and fill in
paymasterApiKeys
withinaaOptions
onSmartAccount
.- The Particle Network AA SDK automatically uses Biconomy's Token Paymaster (for paying gas in ERC20 tokens). Transactions that request it will be able to leverage it without additional configuration.
Initialization
Initializing the Particle Network AA SDK is done primarily through the SmartAccount
object, which can be imported directly from @particle-network/aa
. A new instance of SmartAccount
should be saved to a variable that'll later be used either to construct a custom 1193 provider, or on its own to build and send User Operations.
Specifically, SmartAccount
takes the following parameters:
provider
, an 1193 provider to be called and used whenever specific functions are called (such assendTransaction
,signMessage
, etc.); this can benew ParticleProvider(particle.auth)
if you're using Particle Auth, otherwise, this can be any typical 1193 provider.- Within the body of
SmartAccount
:projectId
, theprojectId
previously retrieved from the Particle dashboard.clientKey
, theclientKey
previously retrieved from the Particle dashboard.appId
, theappId
previously retrieved from the Particle dashboard.aaOptions
, an object containing the following:accountContracts
, an object dictating the smart account implementation(s) to be used. It can be:BICONOMY
, a Biconomy smart account.version
, either1.0.0
or2.0.0
; both versions of Biconomy's smart account implementation are supported.chainIds
, an array of chain IDs in which the smart account is expected to be used.
CYBERCONNECT
, a CyberConnect smart account.version
, currently only1.0.0
is supported forCYBERCONNECT
.chainIds
, an array of chain IDs in which the smart account is expected to be used.
SIMPLE
, a SimpleAccount implementation.version
, currently only1.0.0
is supported forSIMPLE
.chainIds
, an array of chain IDs in which the smart account is expected to be used.
paymasterApiKeys
, an optional array (of objects) to be used when leveraging Biconomy's Paymaster.chainId
, the chain ID being used by the Paymaster.apiKey
, the Biconomy Paymaster API key.
Once SmartAccount
has been initialized and assigned, you can use that assignment (smartAccount
in this case) to call setSmartAccountContract
, reiterating/selecting the smart account implementation choice by passing an object containing name
(BICONOMY
, CYBERCONNECT
, or SIMPLE
), and version
.
import { SmartAccount } from '@particle-network/aa';
const smartAccount = new SmartAccount(provider, {
projectId: 'Particle Network project ID',
clientKey: 'Particle Network client key',
appId: 'Particle Network app ID',
aaOptions: {
accountContracts: { // 'BICONOMY', 'CYBERCONNECT', 'SIMPLE'
BICONOMY: [
{
version: '1.0.0',
chainIds: [x, xx],
},
{
version: '2.0.0',
chainIds: [x, xx],
}
],
CYBERCONNECT: [
{
version: '1.0.0',
chainIds: [x, xx],
}
],
SIMPLE: [
{
version: '1.0.0',
chainIds: [x, xx],
}
],
},
paymasterApiKeys: [{ // Optional
chainId: 1,
apiKey: 'Biconomy Paymaster API Key',
}]
},
});
smartAccount.setSmartAccountContract({ name: 'BICONOMY', version: '2.0.0' });
Examples of Utilization
Get Smart Account
Once you've initialized SmartAccount
and connected your EOA (from Particle's Wallet-as-a-Service or another wallet provider), you can retrieve the linked smart account address (according to the implementation chosen during initialization) with smartAccount.getAddress
, the owner of that smart account (your EOA) with smartAccount.getOwner
, and an object containing smart account information using smartAccount.getAccount
. E.g.:
const address = await smartAccount.getAddress();
const address = await smartAccount.getOwner();
const accountInfo = await smartAccount.getAccount();
Get Fee Quotes
Before sending transactions, if you'd like to manually retrieve fee quotes for user-paid (in native tokens), gasless, and user-paid (in ERC-20 tokens) UserOperations, all with one method call, you can use smartAccount.getFeeQuotes
, passing in either one standard transaction object or an array with multiple (if you'd like to batch transactions). This will return an object containing UserOperation objects (and hashes) for each of the three aforementioned fee payment mechanisms, alongside fee quotes for the token Paymaster, as shown below:
const tx = {
to: '0x...',
value: '0x...'
}
const txs = [
{
to: '0x...',
value: '0x...'
},
{
to: '0x...',
value: '0x...'
}
]
const feeQuotesResult = await smartAccount.getFeeQuotes(tx);
const gaslessUserOp = feeQuotesResult.verifyingPaymasterGasless?.userOp;
const gaslessUserOpHash = feeQuotesResult.verifyingPaymasterGasless?.userOpHash;
const paidNativeUserOp = feeQuotesResult.verifyingPaymasterNative?.userOp;
const paidNativeUserOpHash = feeQuotesResult.verifyingPaymasterNative?.userOpHash;
const tokenPaymasterAddress = feeQuotesResult.tokenPaymaster.tokenPaymasterAddress;
const tokenFeeQuotes = feeQuotesResult.tokenPaymaster.feeQuotes;
Build UserOperation
To build a completed (ready for transaction) UserOperation object and corresponding hash, you can simply call smartAccount.buildUserOperation
, passing in an object containing the following parameters:
tx
, an object, or multiple objects (in an array) representing a standard transaction structure.- Optionally,
feeQuote
, which can be retrieved bygetFeeQuotes
, for utilizing a token Paymaster. - Optionally,
tokenPaymasterAddress
, thetokenPaymasterAddress
property on a result fromgetFeeQuotes
.
const userOpBundle = await smartAccount.buildUserOperation({ tx, feeQuote, tokenPaymasterAddress })
const userOp = userOpBundle.userOp;
const userOpHash = userOpBundle.userOpHash;
Send UserOperation
You can send and push an (already built and complete) UserOperation to the network with smartAccount.sendUserOperation
, which takes an object containing a userOp
object and corresponding userOpHash
. Both of these can be retrieved from passing a structured transaction into buildUserOperation
, as was covered above. E.g.:
const txHash = await smartAccount.sendUserOperation({ userOp, userOpHash });
Wallet Deployment Flow
An undeployed smart account will automatically be deployed upon the first transaction (UserOperation) it sends through the SDK (the deployment transaction is bundled/batched with the other chosen transaction). If you'd like to initiate deployment manually, bypassing automatic deployment, then you can use smartAccount.deployWalletContract
, which will create, request signature for, and send a deployment transaction. The status of deployment can be retrieved with smartAccount.isDeployed
. E.g.:
const isDeploy = await smartAccount.isDeployed();
if (!isDeploy) {
const txHash = await smartAccount.deployWalletContract();
}
Custom 1193 AA Provider
Alternatively, if you'd like to plug the Particle Network AA SDK directly into Ethers or web3.js, you can do so by creating a custom 1193 provider; this will automatically route transactions through the provider and configuration defined within SmartAccount
, converting typical transactions with Ethers, web3.js, and viem into UserOperations, handling everything on the backend.
This can be achieved by creating a new instance of AAWrapProvider
, imported from @particle-network/aa
. AAWrapProvider
takes two parameters:
SmartAccount
, an instance ofSmartAccount
.SendTransactionMode
, optionally defining the mechanism to be used for paying gas fees.SendTransactionMode
can be imported from@particle-network/aa
, and includes:SendTransactionMode.UserPaidNative
, traditional gas payments.SendTransactionMode.Gasless
, sponsored. This will happen automatically for Testnets, and will pull from your previously defined (or configured) Paymaster for Mainnets.SendTransactionMode.UserSelect
, lets the user select which mechanism they'd like to use, including ERC-20 payments.
E.g.:
import { AAWrapProvider, SendTransactionMode, SendTransactionEvent } from '@particle-network/aa';
import Web3 from "web3";
const wrapProvider = new AAWrapProvider(smartAccount, SendTransactionMode.UserPaidNative);
const web3 = new Web3(wrapProvider);
await web3.eth.sendTransaction(tx);
const wrapProvider = new AAWrapProvider(smartAccount, SendTransactionMode.Gasless);
const web3 = new Web3(wrapProvider);
await web3.eth.sendTransaction(tx);
const wrapProvider = new AAWrapProvider(smartAccount, SendTransactionMode.UserSelect);
const web3 = new Web3(wrapProvider);
wrapProvider.once(SendTransactionEvent.Request, (feeQuotesResult) => {
wrapProvider.resolveSendTransaction({
feeQuote: feeQuotesResult.tokenPaymaster.feeQuote[0],
tokenPaymasterAddress: feeQuotesResult.tokenPaymaster.tokenPaymasterAddress
});
wrapProvider.resolveSendTransaction(feeQuotesResult.verifyingPaymasterNative);
if (feeQuotesResult.verifyingPaymasterGasless) {
wrapProvider.resolveSendTransaction(feeQuotesResult.verifyingPaymasterGasless);
}
});
await web3.eth.sendTransaction(tx);
Master reference
For a direct, raw view into every method provided through SmartAccount
, below is a table containing every relevant one alongside specific parameters and a short description. For methods listed that weren't covered in the above examples, live implementation often mimics the common structure covered throughout this document.
Class | Methods | Parameters (* indicates optional) |
---|---|---|
SmartAccount | constructor | provider, config |
SmartAccount | setSmartAccountContract | contract |
SmartAccount | getChainId | |
SmartAccount | getAccountConfig | |
SmartAccount | getPaymasterApiKey | |
SmartAccount | getFeeQuotes | tx |
SmartAccount | buildUserOperation | tx, feeQuote, tokenPaymasterAddress |
SmartAccount | signUserOperation | userOpHash, userOp |
SmartAccount | sendUserOperation | userOpHash, userOp |
SmartAccount | sendSignedUserOperation | userOp, sessionDataParams* |
SmartAccount | sendTransaction | tx, feeQuote, tokenPaymasterAddress |
SmartAccount | getAccount | |
SmartAccount | getAddress | |
SmartAccount | getOwner | |
SmartAccount | isDeployed | |
SmartAccount | deployWalletContract | |
SmartAccount | sendRpc | arg |
SmartAccount | createSessions | options |
SmartAccount | validateSession | targetSession, sessions |