SmartAccount
A SmartAccount
is a signer which can be configured to sign various payloads using a provided secret.
The secret can be in any form, allowing for flexibility when working with different account implementations.
The SmartAccount
is bound to a specific address and provides the ability to define custom method for populating transactions
and custom signing method used for signing messages, typed data, and transactions.
It is compatible with ethers.ContractFactory for deploying contracts/accounts,
as well as with ethers.Contract
for interacting with contracts/accounts using provided ABI along with custom transaction signing logic.
connect
Creates a new instance of SmartAccount
connected to a provider or detached
from any provider if null
is provided.
Inputs
Parameter | Type | Description |
---|---|---|
provider | Provider or null | The provider to connect the SmartAccount to. If null , the SmartAccount will be detached from any provider. |
connect(provider: null | Provider): SmartAccount
Example
import { Wallet, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const sepoliaProvider = Provider.getDefaultProvider(types.Network.Sepolia);
const sepoliaAccount = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, sepoliaProvider);
const mainnetProvider = Provider.getDefaultProvider(types.Network.Mainnet);
const mainnetAccount = sepoliaAccount.connect(mainnetProvider);
constructor
Creates a SmartAccount
instance with provided signer
and provider
.
By default, uses signPayloadWithECDSA
and populateTransactionECDSA
.
Inputs
Parameter | Type | Description |
---|---|---|
signer | SmartAccountSigner | Contains necessary properties for signing payloads. |
provider? | Provider or null | The provider to connect to. Can be null for offline usage. |
constructor(signer: SmartAccountSigner, provider?: null | Provider)
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
getAddress
Returns the address of the account.
getAddress(): Promise<string>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const address = await account.getAddress();
getAllBalances
Returns all token balances of the account.
async getAllBalances(): Promise<BalancesMap>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const balances = await account.getAllBalances();
getBalance
Returns the balance of the account.
Inputs
Parameter | Type | Description |
---|---|---|
token? | Address | The token address to query balance for. Defaults to the native token. |
blockTag | BlockTag | The block tag to get the balance at. Defaults to committed . |
async getBalance(token?: Address, blockTag: BlockTag = 'committed'): Promise<bigint>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const balance = await account.getBalance();
getDeploymentNonce
Returns the deployment nonce of the account.
async getDeploymentNonce(): Promise<bigint>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const nonce = await account.getDeploymentNonce();
populateTransaction
Populates the transaction tx
using the
provided TransactionBuilder
function.
If tx.from
is not set, it sets the value from the getAddress
method which can
be utilized in the TransactionBuilder
function.
Inputs
Parameter | Type | Description |
---|---|---|
tx | TransactionRequest | The transaction that needs to be populated. |
async populateTransaction(tx: TransactionRequest): Promise<TransactionLike>
Example
import { SmartAccount, Provider, types, utils } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const populatedTx = await account.populateTransaction({
type: utils.EIP712_TX_TYPE,
to: "<RECEIVER>",
value: 7_000_000_000,
});
sendTransaction
Sends tx
to the Network. The signTransaction
is called first to ensure transaction is properly signed.
Inputs
Parameter | Type | Description |
---|---|---|
tx | TransactionRequest | The transaction that needs to be sent. |
async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const signedTx = await account.sendTransaction({
to: "<RECEIVER>",
value: ethers.parseEther("1"),
});
signMessage
Signs a message
using the provided PayloadSigner
function.
Inputs
Parameter | Type | Description |
---|---|---|
message | string or Uint8Array | The message that needs to be signed. |
signMessage(message: string | Uint8Array): Promise<string>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const signedMessage = await account.signMessage("Hello World!");
signTransaction
Signs the transaction tx
using the provided PayloadSigner
function,
returning the fully signed transaction. The populateTransaction
method
is called first to ensure that all necessary properties for the transaction to be valid
have been populated.
Inputs
Parameter | Type | Description |
---|---|---|
tx | TransactionRequest | The transaction that needs to be signed. |
async signTransaction(tx: TransactionRequest): Promise<string>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const signedTx = await account.signTransaction({
to: "<RECEIVER>",
value: ethers.parseEther("1"),
});
signTypedData
Signs a typed data using the provided PayloadSigner
function.
Inputs
Parameter | Type | Description |
---|---|---|
domain | ethers.TypedDataDomain | The domain data. |
types | Record<string, ethers.TypedDataField[]> | A map of records pointing from field name to field type. |
value | Record<string, any> | A single record value. |
async signTypedData(
domain: ethers.TypedDataDomain,
types: Record<string, ethers.TypedDataField[]>,
value: Record<string, any>
): Promise<string>
Example
import { SmartAccount, Provider, types } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount({ address: ADDRESS, secret: PRIVATE_KEY }, provider);
const signedTypedData = await account.signTypedData(
{ name: "Example", version: "1", chainId: 270 },
{
Person: [
{ name: "name", type: "string" },
{ name: "age", type: "uint8" },
],
},
{ name: "John", age: 30 }
);
transfer
Transfer ETH or any ERC20 token within the same interface.
Inputs
Parameter | Type | Description |
---|---|---|
transaction.to | Address | The address of the recipient. |
transaction.amount | BigNumberish | The amount of the token to transfer. |
transaction.token? | Address | The address of the token. ETH by default. |
transaction.paymasterParams? | PaymasterParams | Paymaster parameters. |
transaction.overrides? | ethers.Overrides | Transaction's overrides which may be used to pass l2 gasLimit , gasPrice , value , etc. |
async transfer(transaction: {
to: Address;
amount: BigNumberish;
token ? : Address;
paymasterParams?: PaymasterParams;
overrides ? : ethers.Overrides;
}): Promise<ethers.ContractTransaction>
Examples
Transfer ETH.
import { SmartAccount, Wallet, Provider, types } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount(
{address: ADDRESS, secret: PRIVATE_KEY},
provider
);
const transferTx = await account.transfer({
token: utils.ETH_ADDRESS,
to: Wallet.createRandom().address,
amount: ethers.parseEther("0.01"),
});
const receipt = await transferTx.wait();
console.log(`The sum of ${receipt.value} ETH was transferred to ${receipt.to}`);
Transfer ETH using paymaster to facilitate fee payment with an ERC20 token.
import { SmartAccount, Wallet, Provider, utils } from "zksync-ethers";
import { ethers } from "ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const token = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free
const paymaster = "0x13D0D8550769f59aa241a41897D4859c87f7Dd46"; // Paymaster for Crown token
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount(
{address: ADDRESS, secret: PRIVATE_KEY},
provider
);
const transferTx = await account.transfer({
to: Wallet.createRandom().address,
amount: ethers.parseEther("0.01"),
paymasterParams: utils.getPaymasterParams(paymaster, {
type: "ApprovalBased",
token: token,
minimalAllowance: 1,
innerInput: new Uint8Array(),
}),
});
const receipt = await transferTx.wait();
console.log(`The sum of ${receipt.value} ETH was transferred to ${receipt.to}`);
withdraw
Initiates the withdrawal process which withdraws ETH or any ERC20 token from the associated account on L2 network to the target account on L1 network.
Inputs
Parameter | Type | Description |
---|---|---|
transaction.token | Address | The address of the token. ETH by default. |
transaction.amount | BigNumberish | The amount of the token to withdraw. |
transaction.to? | Address | The address of the recipient on L1. |
transaction.bridgeAddress? | Address | The address of the bridge contract to be used. |
transaction.paymasterParams? | PaymasterParams | Paymaster parameters. |
transaction.overrides? | ethers.Overrides | Transaction's overrides which may be used to pass l2 gasLimit , gasPrice , value , etc. |
async withdraw(transaction: {
token: Address;
amount: BigNumberish;
to?: Address;
bridgeAddress?: Address;
paymasterParams?: PaymasterParams;
overrides?: ethers.Overrides;
}): Promise<TransactionResponse>
Examples
Withdraw ETH.
import { SmartAccount, Provider, types, utils } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount(
{address: ADDRESS, secret: PRIVATE_KEY},
provider
);
const withdrawTx = await account.withdraw({
token: utils.ETH_ADDRESS,
amount: 10_000_000n,
});
Withdraw ETH using paymaster to facilitate fee payment with an ERC20 token.
import { SmartAccount, Provider, types, utils } from "zksync-ethers";
const ADDRESS = "<ADDRESS>";
const PRIVATE_KEY = "<PRIVATE_KEY>";
const token = "0x927488F48ffbc32112F1fF721759649A89721F8F"; // Crown token which can be minted for free
const paymaster = "0x13D0D8550769f59aa241a41897D4859c87f7Dd46"; // Paymaster for Crown token
const provider = Provider.getDefaultProvider(types.Network.Sepolia);
const account = new SmartAccount(
{address: ADDRESS, secret: PRIVATE_KEY},
provider
);
const withdrawTx = await account.withdraw({
token: utils.ETH_ADDRESS,
amount: 10_000_000n,
paymasterParams: utils.getPaymasterParams(paymaster, {
type: "ApprovalBased",
token: token,
minimalAllowance: 1,
innerInput: new Uint8Array(),
}),
});