Smart Wallet
Let your users connect to any Smart Wallet.
A Smart Wallet is a wallet that is controlled by a smart contract following the ERC-4337 specification.
thirdweb’s smart wallets have the following features by default:
- Have multiple owners
- Execute transactions (single and batched).
- Send and receive native tokens.
- Send and receive ERC-721 and ERC-1155 NFTs.
- Multicall-able.
- Store contract metadata.
Requirements
1. Smart Wallet Factory Contract
Deployable via the explore page or use your own ERC 4337 compatible factory contract.
Choose the right smart wallet setup for your app. thirdweb offers the following three different kinds of smart wallets:
Account setup | DynamicAccount setup | ManagedAccount setup | |
---|---|---|---|
Upgradeability | Non-upgradeable | Account upgrades controlled locally by account admin. | Account upgrades controlled centrally by account factory admin. |
User persona | Developer wants to issue simple smart wallets to their users. They do not anticipate that users wallets will need any feature upgrades. | Developer wants to issue smart wallets to their users. They do anticipate feature upgrades to user wallets but do not want to push upgrades to user wallets without consent. | Developer wants to issue smart wallets to their users. They do anticipate feature upgrades to user wallets, and want to push upgrades to user wallets for seamless/invisible UX for upgrades. |
2. An API key
Obtain an API key from the thirdweb dashboard.
The API key lets you access thirdweb's bundler and paymaster infrastructure, which is required for the smart wallet to operate and optionally enable gasless transactions.
Learn more about creating an API key and restricting which contracts the smart wallet can interact with here.
The API key will need to have the following settings:
- smart wallet as an enabled service
- The contracts that the smart wallet will interact with must be added to the "Destination Contracts" list. Learn more here
Features
Smart Wallets are controlled by a programmable smart contract rather than by a user as is the case for externally-owned accounts (EOA). This programmable wallet contract can:
- Hold any assets
- Pay for its own gas
- Execute transactions in batches
- Have multiple owners
- Assign roles and permissions
And more!
Smart Wallets can be customized to each application's needs using the Solidity SDK.
The SmartWallet API is composable - you can use in your own ERC 4337 compatible factory contract and you can also use your own bundler or paymaster services.
Bundler & Paymaster Services
Thirdweb's bundler and paymaster services are used to fund and send transactions. To use these services, you need to setup a billing account on the thirdweb dashboard Settings page.
Smart Wallet is free to use with your own account abstraction infrastructure. To use your own paymaster & bundler,
pass the bundlerUrl
, paymasterUrl
and paymasterAPI
options to the SmartWallet
:
class MyPaymaster extends PaymasterAPI {
async getPaymasterAndData(
userOp: Partial<UserOperationStruct>,
): Promise<string> {
// your implementation, must return the signed paymaster data
}
}
const config: SmartWalletConfig = {
chain,
gasless,
factoryAddress,
clientId,
bundlerUrl: "your-bundler-url",
paymasterUrl: "your-paymaster-url",
paymasterAPI: new MyPaymaster(),
};
Pricing & Billing
To use thirdweb's account abstraction infrastructure, you need to setup a billing account on the thirdweb dashboard Settings page.
Free Tier
- Ideal for small teams and individuals who require basic features.
- Features:
- Bundler: 10% premium on top of network fee based on your usage.
- Paymaster: 10% premium on top of network fee based on your usage.
Pro Tier
- Ideal for teams that require a higher level of customization and support
- Features:
- Bundler: 10% premium on top of network fee based on your usage.
- Paymaster: 10% premium on top of network fee based on your usage.
Find more information on the different billing tiers by visiting thirdweb's pricing page.
Templates
Clone these templates to quickly deploy Smart Wallets and connect to them.
Supported chains
With a thirdweb API key, you get access to bundler and paymaster infrastructure on the following chains:
View supported chains
- Polygon
- Optimism
- Base
- Arbitrum
- Linea
- Goerli
- Sepolia
- Mumbai
- Base Goerli
- Optimism Goerli
- Arbitrum Goerli
- Linea Testnet
- Celo Alfajores Testnet
more chains are added every week, to get support for a specific chain contact us.
Usage
To connect to a smart wallet, a personal wallet (acting as the key to the smart wallet) must first be connected.
import { LocalWallet, SmartWallet } from "@thirdweb-dev/wallets";
import { Goerli } from "@thirdweb-dev/chains";
// First, connect the personal wallet, which can be any wallet (metamask, walletconnect, etc.)
// Here we're just generating a new local wallet which can be saved later
const personalWallet = new LocalWallet();
await personalWallet.generate();
// Setup the Smart Wallet configuration
const config: SmartWalletConfig = {
chain: Goerli, // the chain where your smart wallet will be or is deployed
factoryAddress: "{{factory_address}}", // your own deployed account factory address
clientId: "YOUR_CLIENT_ID", // Use client id if using on the client side, get it from dashboard settings
secretKey: "YOUR_SECRET_KEY", // Use secret key if using on the server, get it from dashboard settings
gasless: true, // enable or disable gasless transactions
};
// Then, connect the Smart wallet
const wallet = new SmartWallet(config);
await wallet.connect({
personalWallet,
});
// You can then use this wallet to perform transactions via the SDK
const sdk = await ThirdwebSDK.fromWallet(wallet, Goerli);
Configuration
Here's the full configuration object you can pass when instantiating the SmartWallet
class.
Mandatory properties
chain
The chain that the Smart Wallet contract is deployed to.
Either a Chain
object, from the @thirdweb-dev/chains
package, a chain name, or an RPC URL.
factoryAddress
The address of the Smart Wallet Factory contract.
Must be a string
.
clientId or secretKey (recommended)
Your API key can be obtained from the thirdweb dashboard.
If you're using your own bundler and paymaster, you can set this to an empty string.
You can use either the clientId
or the secretKey
depending on whether your application is client or server side.
Must be a string
.
gasless
Whether to turn on or off gasless transactions.
- If set to
true
, all gas fees will be paid by a paymaster. - If set to
false
, all gas fees will be paid by the Smart Wallet itself (needs to be funded).
Must be a boolean
.
Optional properties
clientId or secretKey (optional)
Provide clientId
or secretKey
to use the thirdweb RPCs for given chains
If you are using the SmartWallet
in a in frontend - provide a clientId
, If you are using the SmartWallet
in backend - you can provide a secretKey
.
You can create a clientId
/ secretKey
from thirdweb dashboard.
factoryInfo
Customize how the Smart Wallet Factory contract is interacted with. If not provided, the default functions will be used.
Must be a object
. The object can contain the following properties:
createAccount
- a function that returns the transaction object to create a new Smart Wallet.getAccountAddress
- a function that returns the address of the Smart Wallet contract given the owner address.abi
- optional ABI. If not provided, the ABI will be auto-resolved.
Example:
const config: SmartWalletConfig = {
chain,
gasless,
factoryAddress,
clientId,
factoryInfo: {
createAccount: async (factory, owner) => {
return factory.prepare("customCreateAccount", [
owner,
getExtraData(),
]);
},
getAccountAddress: async (factory, owner) => {
return factory.call("getAccountAddress", [owner]);
},
abi: [...]
},
};
accountInfo
Customize how the Smart Wallet Account contract is interacted with. If not provided, the default functions will be used.
Must be a object
. The object can contain the following properties:
execute
- a function that returns the transaction object to execute an arbitrary transaction.getNonce
- a function that returns the current nonce of the account.abi
- optional ABI. If not provided, the ABI will be auto-resolved.
Example:
const config: SmartWalletConfig = {
chain,
gasless,
factoryAddress,
clientId,
accountInfo: {
execute: async (account, target, value, data) => {
return account.prepare("customExecute", [
target, value, data
]);
},
getNonce: async (account) => {
return account.call("getNonce");
},
abi: [...]
},
};
bundlerUrl
Your own bundler URL to send user operations to. Uses thirdweb's bundler by default.
Must be a string
.
paymasterUrl
Your own paymaster URL to send user operations to for gasless transactions. Uses thirdweb's paymaster by default.
Must be a string
.
paymasterAPI
Fully customize how the paymaster data is computed.
Must be a PaymasterAPI
class.
Example:
class MyPaymaster extends PaymasterAPI {
async getPaymasterAndData(
userOp: Partial<UserOperationStruct>,
): Promise<string> {
// your implementation, must return the signed paymaster data
}
}
const config: SmartWalletConfig = {
chain,
gasless,
factoryAddress,
clientId,
paymasterAPI: new MyPaymaster(),
};
entryPointAddress
The entrypoint contract address. Uses v0.6 by default.
Must be a string
.
chains
Provide an array of chains you want to support.
Must be an array of Chain
objects, from the @thirdweb-dev/chains
package.
Defaults to thirdweb's default chains.
import { SmartWallet } from "@thirdweb-dev/wallets";
import { Ethereum } from "@thirdweb-dev/chains";
const wallet = new SmartWallet(
{
chains: [Ethereum],
},
);
dappMetadata
Information about your app that the wallet will display when your app tries to connect to it.
Must be an object containing name
, url
and optionally description
and logoUrl
properties.
import { SmartWallet } from "@thirdweb-dev/wallets";
const wallet = new SmartWallet({
dappMetadata: {
name: "thirdweb powered dApp",
url: "https://thirdweb.com",
description: "thirdweb powered dApp", // optional
logoUrl: "https://thirdweb.com/favicon.ico", // optional
},
});
walletId
An ID for the wallet that is used to store the wallet in the walletStorage
.
import { SmartWallet } from "@thirdweb-dev/wallets";
const wallet = new SmartWallet(
{
walletId: "wallet-id",
},
);
enableConnectApp
Enables the wallet to be available to listen for WalletConnect events.
Note that to fully support this there needs to be a UI component to handle WalletConnect uris (wc://
) and pass it to the wallet.
Defaults to false
.
wcVersion
You can choose the WalletConnect version to support. Either 'v1'
or 'v2'
.
Defaults to 'v2'
.
Note that WalletConnect v1 is no longer maintained and will be shutdown on June 28th, 2023
walletConnectV2ProjectId
The WalletConnect V2 projectId
. You can get one in the WalletConnect docs.
Defaults to a common projectId
set by thirdweb. This should be ok for testing but note that if you want to deploy your mobile app it may make sense to create your own as WalletConnect may throttle traffic coming from the same projectId
.
walletConnectV2RelayUrl
Define a custom Relay Server URL. Defaults to "wss://relay.walletconnect.com"
walletConnectWalletMetadata
Metadata that will be displayed in the dApp once your SmartWallet is connected to it.
{
name: string; // defaults to: "Thirdweb Smart Wallet",
description: string; // defaults to: "Thirdweb Smart Wallet",
url: string: // defaults to: "https://thirdweb.com",
icons: string[]; // defaults to: ["https://thirdweb.com/favicon.ico"],
};
wcStorage
Synchronous storage needed by WalletConnect V2.
Defaults to synchronous storage implemented localStorage
.
Methods
Inherits all the public methods from the AbstractClientWallet
class.
connect
Connect the smart wallet to your app using a personal wallet.
This personal wallet must be connected before connecting to a Smart wallet.
import { CoinbaseWallet, SmartWallet } from "@thirdweb-dev/wallets";
import { Ethereum } from "@thirdweb-dev/chains";
// First, connect the personal wallet
const personalWallet = new CoinbaseWallet();
const personalWalletAddress = await personalWallet.connect();
// Then, connect the Smart wallet
const smartWallet = new SmartWallet(config);
const smartWalletAddress = await smartWallet.connect({
personalWallet,
});
Configuration
personalWallet
The instance of a personal wallet that can sign transactions on the Smart Wallet.
Must be an EVMWallet
instance such as CoinbaseWallet
or MetamaskWallet
.
Returns a string
containing the wallet address, or throws an error if the connection fails.
string;
execute
Execute a single transaction as the connected Smart Wallet.
// first, connect the Smart wallet
const wallet = new SmartWallet(config);
await wallet.connect({
personalWallet,
});
// Then you can execute transactions directly
const transaction = prepareTransaction();
await wallet.execute(transaction);
Configuration
transaction
The transaction to execute. Must be of type Transaction
from the @thirdweb-dev/sdk
package.
Creating these transactions can be done easily using the Transaction Builder from the thirdweb SDK.
Returns a TransactionResult
containing the transaction receipt.
TransactionResult;
executeBatch
Execute multiple transactions as the connected Smart Wallet at once, only requiring one signature from the personal wallet.
// first, connect the Smart wallet
const wallet = new SmartWallet(config);
await wallet.connect({
personalWallet,
});
// Then you can execute multiple transactions at once
const transactions = [
prepareTransaction1(),
prepareTransaction2(),
prepareTransaction3(),
];
await wallet.executeBatch(transactions);
Configuration
transactions
An array of transactions to execute. Must be of type Transaction[]
from the @thirdweb-dev/sdk
package.
Creating these transactions can be done easily using the Transaction Builder from the thirdweb SDK.
Returns a TransactionResult
containing the transaction receipt.
TransactionResult;