Migration Guide
Below are the steps to migrate your project from older versions of the aa-sdk
and account-kit
to the latest version.
Migrating to version v4.x.x
Packages
All of the v3 packages have been renamed or removed. The new packages are as follows:
@alchemy/aa-core
->@aa-sdk/core
@alchemy/aa-ethers
->@aa-sdk/ethers
@alchmey/aa-alchemy
-> split into@account-kit/signer
,@account-kit/infra
,@account-kit/core
,@account-kit/react
@alchemy/aa-accounts
->@account-kit/smart-contracts
@alchemy/aa-signers
-> removed
AA signers
The @alchemy/aa-signers
package has been deprecated. The signers in that package are still compatible with v4, but they will not be receiving future support and the interfaces are not guaranteed to work with future versions of the SDK.
If you want to integrate a 3rd party signer, it's still very easy if the signer exposes an EIP-1193 interface. See this guide.
Alchemy Transport
The new Transport
type: AlchemyTransport
has been added. This impacts how Alchemy clients, middleware, and configs are created.
For Smart Account Clients, the create*AlchemyClient
methods have been updated to take a new transport
property (of type AlchemyTrasnport
) instead of rpcUrl
or apiKey
directly.
The alchemyFeeEstimator
and alchemyUserOperationSimulator
middleware methods now no longer take in a ClientWithAlchemyMethods
(returned by createAlchemyPublicRpcClient
) and instead take in an AlchemyTransport
as well.
The AlchemyTransport
is a type of viem
transport that makes it easier to configure your communication with Alchemy RPC. It supports splitting traffic between Alchemy's AA infra and other Node providers (if needed). The AlchemyTransport
has been added to @account-kit/infra
and is exported as alchemy
.
Creating a config with @account-kit/core
or @account-kit/react
has been updated to accept an AlchemyTransport
and the parameters of createConfig
have been simplified as a result. You will now need to replace your rpcUrl
or apiKey
params with transport: alchemy({...})
.
For more detailed examples, see the relevant Quickstart guides, depending on which package you are using. If you don't know where to start, checkout the React Quickstart.
Hooks: useSendTransaction
and useSendTransactions
removed
These methods have been removed since useSendUserOperation
can be used to send transactions and user operations. If you were using useSendTransaction
or useSendTransactions
, you can replace them with
useSendUserOperation
and pass in waitForTxn: true
so that all sendUserOperation
calls will wait for the transaction to be mined.
Utils: verifyEIP6492Signature
removed
This method has been removed. Use verifyMessage
from viem.
Utils: defineReadOnly
removed
This method is no longer used internally and has been removed. The reference impl can be found within ethers.js
.
Utils: getChain
removed
This method was not super efficient and maintainable because it was importing all the chains from viem. That meant that if you used this method, it risked increasing your bundle size massively. Now all methods require a Chain
instead of chainId
in some places.
Ethers: Chain
required param
Certain methods required a chainId
which would be converted into a Chain
using the above method. This has been removed for the reasons above.
Alchemy Chain
defs
The Alchemy Chain
definitions have been moved from @aa-sdk/core
to @account-kit/infra
.
Ethers: getPublicErc4337Client
→ getBundlerClient
This method on the AccountSigner
has been renamed.
Accounts: Default LightAccount version is now v2.0.0
If you were creating LightAccounts for users without explicitly specifying the version (ie. in createLightAccount
or useAccount
or useSmartAccountClient
), you should manually specify the previous default of v1.1.0
.
Accounts: create*Client
methods now have the account parameters un-nested
Previously, the create*Client
(ie. createLightAccountClient
) methods used to have an account
parameter which took in the params for the underlying account. This is resulted in different APIs for both the corresponding create*AlchemyClient
(ie. createLightAccountAlchemyClient
) which had these as top-level parameters.
If you were using createLightAccountClient
or similar methods, you should now pass the account parameters as top-level parameters.
Accounts: getNonce
-> getAccountNonce
Due to a clash in the typing of viem's Account
type, the getNonce
method has been renamed to getAccountNonce
.
Viem Version
The version of viem has been updated to 2.20.0
and been added as a peer dependency. If you have viem listed as a dependency, make sure it is updated to 2.20.0
.
Dropped CJS support
Due to our dependency on wagmi
in the core
and react
packages, those packages no longer support cjs
builds. To keep things consistent across all of our packages, we've dropped CJS support in all the packages in this repo. See this guide on how to migrate from CJS to ESM.
Migrating to version 3.x.x
This version update brings a lot of breaking changes. As we began to support Modular Accounts and their interfaces, we realized that the previous version of the SDK was not as flexible as it could have been to handle the modularity of the new account interfaces.
To address this, version 3.x.x of the SDK switches to an approach more idiomatic to viem
.
Viem Version
We have updated our dependency to viem v2.x.x. This means you will need to update your project to use >= v2.5.0 of viem.
Client: SmartAccountProvider
→ SmartAccountClient
The biggest change is that the SmartAccountProvider
class has been removed and replaced with a SmartAccountClient
type that extends viem
's Client
. To get started with the new clients, you can do the following:
Client: removed sign*With6492
methods
The signMessageWith6492
and signTypedDataWith6492
methods have been removed from the SmartAccountClient
. Now, all clients will always use 6492 when signing messages. However, if you need to sign messages without 6492, you can still use the client.account.signMessage
methods.
Client: checkGasSponsorshipEligibility
return type updated
The checkGasSponsorshipEligibility
methods now returns an object that includes the eligibility status of a User Operation as well as the UserOperationStruct
that the eligibility is for. This now ensures that checking for sponsorhip doesn't cause you to use up your
gas sponsorship limits. If your User Operation is eligible, then you can sign the UO with client.signUserOperation
and send it with client.sendRawUserOperation
.
import { SmartAccountProvider } from "@aa-sdk/core";
import { getDefaultEntryPointAddress } from "@aa-sdk/core";
import { http } from "viem";
import { sepolia } from "@aa-sdk/core";
const provider = new SmartAccountProvider({
const client = createSmartAccountClient({
rpcProvider: "RPC_URL",
transport: http("RPC_URL"),
chain: sepolia,
entryPointAddress: getDefaultEntryPointAddress(sepolia),
});
Client: Removal of with*
middleware override functions
The SmartAccountProvider
in previous versions had a number of with*
functions that mapped to the corresponding middleware functions on the provider.
The concept of the middlewares is still present in this version, but their configuration has been moved to the SmartAccountClient
creator. For example,
import { SmartAccountProvider } from "@aa-sdk/core";
import { getDefaultEntryPointAddress } from "@aa-sdk/core";
import { http } from "viem";
import { sepolia } from "@aa-sdk/core";
const provider = new SmartAccountProvider({
const client = createSmartAccountClient({
rpcProvider: "RPC_URL",
transport: http("RPC_URL"),
chain: sepolia,
entryPointAddress: getDefaultEntryPointAddress(sepolia),
}).withGasEstimator(async () => ({
gasEstimator: async (struct) => ({
...struct,
callGasLimit: 0n,
preVerificationGas: 0n,
verificationGasLimit: 0n,
}),
});
Client: signature changes on methods
To support the various ways of connecting to a smart account, the signatures of the methods on SmartAccountClient
have changed. Almost all methods now have an optional param for account
and have been converted into single argument functions that take an object with the their properties instead.
Account: BaseSmartContractAccount
→ SmartContractAccount
The next big change is the removal of the class-based BaseSmartContractAccount
that all accounts extended from. This has been replaced with a SmartContractAccount
type that extends viem
's Account
, and instantiation of an account is now an async
action. To get started with the new accounts (using LightAccount
as an example), you will have to make the following changes:
import {
LightSmartContractAccount,
createLightAccount,
getDefaultLightAccountFactoryAddress,
} from "@account-kit/smart-contracts";
import {
LocalAccountSigner,
type Hex,
} from "@aa-sdk/core";
import { sepolia } from "@aa-sdk/core";
const chain = sepolia;
const account = new LightSmartContractAccount({
const account = await createLightAccount({
rpcClient: client,
transport: http("RPC_URL"),
signer,
chain,
factoryAddress: getDefaultLightAccountFactoryAddress(chain),
});
Account: Connecting to a Smart Account
In earlier versions, a provider could not be used with a smart account until it was connected to one using .connect
. In version 3.x.x, you have the option of keeping the two disconnected and passing the account to the client methods directly. You also have the option to hoist the account
so that you don't have to pass the account to every method.
Option 1: Passing the Account to the Client Methods
import { createLightAccount } from "@account-kit/smart-contracts";
import {
createBundlerClient,
createSmartAccountClientFromExisting
LocalAccountSigner,
type Hex,
} from "@aa-sdk/core";
import { sepolia } from "@aa-sdk/core";
import { custom, http } from "viem";
const chain = sepolia;
const client = createBundlerClient({
chain,
transport: http("JSON_RPC_URL"),
});
// createSmartAccountClientFromExisting is a helper method that allows you
// to reuse a JSON RPC client to create a Smart Account client.
const smartAccountClient = createSmartAccountClientFromExisting({
client,
});
const account = await createLightAccount({
signer,
chain,
transport: custom(client),
});
const { hash } = await smartAccountClient.sendUserOperation({
uo: {
target: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
data: "0x",
value: 10n,
},
account,
});
Option 2: Hoisting the Account
Hoisting the account is similar to using .connect
in previous versions. You simply create your client with an account passed in to it.
import { createLightAccount } from "@account-kit/smart-contracts";
import {
createBundlerClient,
createSmartAccountClientFromExisting
LocalAccountSigner,
type Hex,
} from "@aa-sdk/core";
import { sepolia } from "@aa-sdk/core";
import { http, custom } from "viem";
const chain = sepolia;
const client = createBundlerClient({
chain,
transport: http("JSON_RPC_URL"),
});
const account = await createLightAccount({
signer,
transport: custom(client),
chain,
});
const smartAccountClient = createSmartAccountClientFromExisting({
account,
client,
});
const { hash } = await smartAccountClient.sendUserOperation({
uo: {
target: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
data: "0x",
value: 10n,
},
account,
});
Account: Custom Accounts
In prior versions, using your own smart contract account implementations required that you extend BaseSmartContractAccount
. In version 3.x.x, you can use the toSmartContractAccount
method which will allow you to use any account with SmartAccountClient
. toSmartContractAccount
has the form of:
type toSmartContractAccount = <
Name extends string = string,
TTransport extends Transport = Transport
>({
transport,
chain,
source,
entryPointAddress,
accountAddress,
getAccountInitCode,
signMessage,
signTypedData,
encodeBatchExecute,
encodeExecute,
getDummySignature,
signUserOperationHash,
encodeUpgradeToAndCall,
}: ToSmartContractAccountParams<Name, TTransport>) => Promise<
SmartContractAccount<Name>
>;
Account: SimpleAccount and NaniAccount initialization params
chain
and transport
have been added to the constructor and rpcClient
has been removed.
Account: SimpleAccount and LightAccount intialization params
index
is now called salt
Signer: signTypedData
signature change
The signTypedData
method found on SmartAccountSigner
has been updated to match the signature found on SmartContractAccount
and viem's Account
.
(params: SignTypedDataParams) => Promise<Hex>;
<
const TTypedData extends TypedData | { [key: string]: unknown },
TPrimaryType extends string = string
>(
params: TypedDataDefinition<TTypedData, TPrimaryType>
) => Promise<Hex>;
Ethers: Removed methods
The with*
methods have been removed from the Provider and Signer classes.
The connect
methods has been removed in favor of immutable properties on the Provider and Signer classes. See updated AccountSigner constructor below.
Ethers: getPublicErc4337Client
→ getBundlerClient
The getPublicErc4337Client
method has been renamed to getBundlerClient
to match the naming found in aa-core
.
Ethers: Updated Signer Adapter constructor
The AccountSigner
now takes in a SmartContractAccount
as a param in its constructor.
Core: Transition from Percent
to Multiplier
api and types
Percent
The Percent
type and PercentSchema
have been removed in favor of the Multiplier
type and MultiplierSchema
.
Going forward when using the feeOptions, you can specify the Multiplier
type instead of a Percent
. The Multiplier
type is a number that represents direct multipliaction of the estimation. For example, 0.1
is 10% of the estimated value and 1
is 100% of the estimated value.
createModularAccountAlchemyClient({
...
opts: {
...
// The maxFeePerGas and maxPriorityFeePerGas estimated values will now be multipled by 1.5
feeOptions: {
// This was previously { percent: 50n }
maxFeePerGas: { multiplier: 1.5 },
// This was previously { percent: 25n }
maxPriorityFeePerGas: { multiplier: 1.25 },
},
},
});