@thirdweb-dev/wallets
Version:
<p align="center"> <br /> <a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/legacy_packages/sdk/logo.svg?raw=true" width="200" alt=""/></a> <br /> </p> <h1 align="center">thirdweb Wallet SDK</h1> <p align="center"> <a h
650 lines (621 loc) • 22.4 kB
JavaScript
import { _ as _defineProperty } from '../../../../dist/defineProperty-350fc508.browser.esm.js';
import { a as AbstractClientWallet } from '../../../../dist/base-a72d5b10.browser.esm.js';
import { w as walletIds } from '../../../../dist/walletIds-dff6dced.browser.esm.js';
import { getValidChainRPCs } from '@thirdweb-dev/chains';
import { utils } from 'ethers';
import { e as isZkSyncChain, c as checkContractWalletSignature } from '../../../../dist/utils-f58e7acc.browser.esm.js';
export { D as DEFAULT_FACTORY_ADDRESS, g as getAllSigners, a as getAllSmartWallets, b as getSmartWalletAddress, d as getUserOpReceipt, i as isSmartWalletDeployed, e as isZkSyncChain } from '../../../../dist/utils-f58e7acc.browser.esm.js';
import '../../abstract/dist/thirdweb-dev-wallets-evm-wallets-abstract.browser.esm.js';
import 'eventemitter3';
import '@thirdweb-dev/sdk';
import '../../../../dist/headers-733a8199.browser.esm.js';
import '../../../../dist/url-a45219bd.browser.esm.js';
import '@account-abstraction/contracts';
/**
* Let your users connect to a [Smart Wallet](https://portal.thirdweb.com/glossary/smart-wallet).
*
* A Smart Wallet is a wallet that is controlled by a smart contract following the [ERC-4337 specification](https://eips.ethereum.org/EIPS/eip-4337).
*
* _For a complete overview of Smart Wallets, visit the [Smart Wallet SDK documentation](https://portal.thirdweb.com/wallets/smart-wallet)_
*
* #### References
* - [How to use smart wallets with the thirdweb SDKs.](https://portal.thirdweb.com/wallets/smart-wallet)
* - [Learn more about what a smart wallet is and how it works.](https://portal.thirdweb.com/wallets/smart-wallet/how-it-works)
* - [Using the thirdweb account abstraction infrastructure.](https://portal.thirdweb.com/wallets/smart-wallet/infrastructure)
*
* @example
*
* To connect to a smart wallet, a personal wallet (acting as the key to the smart wallet) must first be connected.
*
* ```ts
* 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);
* ```
*
* @wallet
*/
class SmartWallet extends AbstractClientWallet {
/**
* @internal
*/
get walletName() {
return "Smart Wallet";
}
/**
*
* @param options - The `options` object includes the following properties:
* ### Required Properties
*
* #### chain
* The chain that the Smart Wallet contract is deployed to.
*
* Either a `Chain` object, from the [`@thirdweb-dev/chains`](https://www.npmjs.com/package/\@thirdweb-dev/chains) package, a chain name, or an RPC URL.
*
*
* #### factoryAddress
* The address of the Smart Wallet Factory contract.
*
* 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 (recommended)
* Your API key can be obtained from the [thirdweb dashboard](https://thirdweb.com/create-api-key).
*
* 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`.
*
* #### 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.
*
* ```javascript
* 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.
*
* ```javascript
* 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.
*
* ```javascript
* 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,
* // highlight-start
* paymasterAPI: new MyPaymaster(),
* // highlight-end
* };
* ```
*
*
* #### entryPointAddress
* The entrypoint contract address. Uses v0.6 by default.
*
* Must be a `string`.
*
* #### deployOnSign
* Whether to deploy the smart wallet when the user signs a message. Defaults to true.
*
* Must be a `boolean`.
*
* #### chains
* Provide an array of chains you want to support.
*
* Must be an array of `Chain` objects, from the [`@thirdweb-dev/chains`](https://www.npmjs.com/package/\@thirdweb-dev/chains) package.
*
* Defaults to thirdweb's [default chains](/react/react.thirdwebprovider#default-chains).
*
* #### 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.
*
* ```javascript
* 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
* },
* });
* ```
*
*/
constructor(options) {
if (options.clientId && typeof options.chain === "object") {
try {
options.chain = {
...options.chain,
rpc: getValidChainRPCs(options.chain, options.clientId)
};
} catch {}
}
super(SmartWallet.id, {
...options
});
}
async getConnector() {
if (!this.connector) {
if (this.options && (await isZkSyncChain(this.options.chain, this.options.clientId, this.options.secretKey))) {
const {
ZkSyncConnector
} = await import('../../../../dist/zk-connector-fada2157.browser.esm.js');
this.connector = new ZkSyncConnector(this.options);
} else {
const {
SmartWalletConnector
} = await import('../../../connectors/smart-wallet/dist/thirdweb-dev-wallets-evm-connectors-smart-wallet.browser.esm.js');
this.connector = new SmartWalletConnector(this.options);
}
}
return this.connector;
}
/**
* Get the personal wallet that is connected to the Smart Wallet.
* @example
* ```ts
* const personalWallet = wallet.getPersonalWallet();
* ```
*/
getPersonalWallet() {
return this.connector?.personalWallet;
}
/**
* Check whether the connected signer can execute a given transaction using the smart wallet.
* @param transaction - The transaction to execute using the smart wallet.
* @returns `Promise<true>` if connected signer can execute the transaction using the smart wallet.
*/
async hasPermissionToExecute(transaction) {
const connector = await this.getConnector();
return connector.hasPermissionToExecute(transaction);
}
/**
* Send a single transaction without waiting for confirmations
* @param transaction - the transaction to send
* @param options - optional transaction options
* @returns The transaction result
*/
async send(transaction, options) {
const connector = await this.getConnector();
return connector.send(transaction, options);
}
/**
* Execute a single transaction and wait for confirmations
*
* @example
* ```javascript
* const transaction = prepareTransaction();
* await wallet.execute(transaction);
* ```
*
* @param transaction -
* The transaction to execute. Must be of type `Transaction` from the [`@thirdweb-dev/sdk`](https://www.npmjs.com/package/\@thirdweb-dev/sdk) package.
*
* Creating these transactions can be done easily using the [Transaction Builder](https://portal.thirdweb.com/typescript/v4/interact#prepare) from the thirdweb SDK.
* @param options - optional transaction options
* @returns `TransactionResult` containing the transaction receipt.
*/
async execute(transaction, options) {
const connector = await this.getConnector();
return connector.execute(transaction, options);
}
/**
* Send a multiple transaction in a batch without waiting for confirmations
* @param transactions -
* An array of transactions to send. Must be of type `Transaction[]` from the [`@thirdweb-dev/sdk`](https://www.npmjs.com/package/\@thirdweb-dev/sdk) package.
*
* Creating these transactions can be done easily using the [Transaction Builder](typescript/sdk.smartcontract.prepare) from the thirdweb SDK.
* @param options - optional transaction options
* @returns `TransactionResult` containing the transaction receipt.
*/
async sendBatch(transactions, options) {
const connector = await this.getConnector();
return connector.sendBatch(transactions, options);
}
/**
* Execute multiple transactions in a single batch and wait for confirmations, only requiring one signature from the personal wallet.
*
* ```javascript
* // Then you can execute multiple transactions at once
* const transactions = [
* prepareTransaction1(),
* prepareTransaction2(),
* prepareTransaction3(),
* ];
* await wallet.executeBatch(transactions);
* ```
*
* @param transactions -
* An array of transactions to execute. Must be of type `Transaction[]` from the [`@thirdweb-dev/sdk`](https://www.npmjs.com/package/\@thirdweb-dev/sdk) package.
*
* Creating these transactions can be done easily using the [Transaction Builder](typescript/sdk.smartcontract.prepare) from the thirdweb SDK.
*
* @param options - optional transaction options
* @returns `TransactionResult` containing the transaction receipt.
*/
async executeBatch(transactions, options) {
const connector = await this.getConnector();
return connector.executeBatch(transactions, options);
}
/**
* Send a single raw transaction without waiting for confirmations
* @param transaction - the transaction to send
* @param options - optional transaction options
* @returns The transaction result
*/
async sendRaw(transaction, options) {
const connector = await this.getConnector();
return connector.sendRaw(transaction, options);
}
/**
* Execute a single raw transaction and wait for confirmations
* @param transaction - the transaction to execute
* @param options - optional transaction options
* @returns The transaction receipt
*/
async executeRaw(transaction, options) {
const connector = await this.getConnector();
return connector.executeRaw(transaction, options);
}
/**
* Estimate the gas cost of a single transaction
* @param transaction - the transaction to estimate
* @param options - optional transaction options
* @returns
*/
async estimate(transaction, options) {
const connector = await this.getConnector();
return connector.estimate(transaction, options);
}
/**
* Estimate the gas cost of a batch of transactions
* @param transactions - the transactions to estimate
* @param options - optional transaction options
* @returns
*/
async estimateBatch(transactions, options) {
const connector = await this.getConnector();
return connector.estimateBatch(transactions, options);
}
/**
* Estimate the gas cost of a single raw transaction
* @param transactions - the transactions to estimate
* @param options - optional transaction options
* @returns
*/
async estimateRaw(transactions, options) {
const connector = await this.getConnector();
return connector.estimateRaw(transactions, options);
}
/**
* Estimate the gas cost of a batch of raw transactions
* @param transactions - the transactions to estimate
* @param options - optional transaction options
* @returns
*/
async estimateBatchRaw(transactions, options) {
const connector = await this.getConnector();
return connector.estimateBatchRaw(transactions, options);
}
/**
* Send multiple raw transaction in a batch without waiting for confirmations
* @param transactions - the transactions to send
* @param options - optional transaction options
* @returns The transaction result
*/
async sendBatchRaw(transactions, options) {
const connector = await this.getConnector();
return connector.sendBatchRaw(transactions, options);
}
/**
* Execute multiple raw transactions in a single batch and wait for confirmations
* @param transactions - the transactions to execute
* @param options - optional transaction options
* @returns The transaction receipt
*/
async executeBatchRaw(transactions, options) {
const connector = await this.getConnector();
return connector.executeBatchRaw(transactions, options);
}
/**
* Manually deploy the smart wallet contract. If already deployed this will throw an error.
*
* Note that this is not necessary as the smart wallet will be deployed automatically on the first transaction the user makes.
*
* @example
* ```ts
* const tx = await wallet.deploy();
* ```
* @param options - optional transaction options
* @returns The transaction receipt
*/
async deploy(options) {
const connector = await this.getConnector();
return connector.deploy(options);
}
/**
* Manually deploy the smart wallet contract. If already deployed this will do nothing.
* Note that this is not necessary as the smart wallet will be deployed automatically on the first transaction the user makes.
*
* @example
* ```ts
* await wallet.deployIfNeeded();
* ```
* @param options - optional transaction options
* @returns The transaction receipt
*/
async deployIfNeeded(options) {
const connector = await this.getConnector();
return connector.deployIfNeeded(options);
}
/**
* Check if the smart wallet contract is deployed
* @example
* ```ts
* const isDeployed = await wallet.isDeployed();
* ```
*
* @returns `true` if the smart wallet contract is deployed
*/
async isDeployed() {
const connector = await this.getConnector();
if (connector.chainId && (await isZkSyncChain(connector.chainId))) {
return true;
}
return connector.isDeployed();
}
/**
* Create and add a session key to the Smart Wallet with specific permissions.
* @example
* ```javascript
* // Then you can add session keys with permissions
* await wallet.createSessionKey(
* "0x...", // the session key address
* {
* approvedCallTargets: ["0x..."], // the addresses of contracts that the session key can call
* nativeTokenLimitPerTransaction: 0.1, // the maximum amount of native token (in ETH) that the session key can spend per transaction
* startDate: new Date(), // the date when the session key becomes active
* expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000); // the date when the session key expires
* }
* );
* ```
*
* @param keyAddress - The address of the session key to add to the Smart Wallet.
*
* @param permissions -
* The specific permissions to give to the session key.
* Must be of type `SignerPermissionsInput` from the [`@thirdweb-dev/sdk`](https://www.npmjs.com/package/\@thirdweb-dev/sdk) package.
*
* ```typescript
* {
* startDate: Date;
* expirationDate: Date;
* nativeTokenLimitPerTransaction: number;
* approvedCallTargets: string[];
* }
* ```
*/
async createSessionKey(keyAddress, permissions) {
const connector = await this.getConnector();
return connector.grantPermissions(keyAddress, permissions);
}
/**
* Revoke a session key from the Smart Wallet.
* @example
* ```javascript
* await wallet.revokeSessionKey(
* "0x...", // the session key address
* );
* ```
*
* @param keyAddress - The address of the session key to revoke.
*/
async revokeSessionKey(keyAddress) {
const connector = await this.getConnector();
return connector.revokePermissions(keyAddress);
}
/**
* Add another admin to the smart wallet.
* @param adminAddress - The address of the admin to add.
*/
async addAdmin(adminAddress) {
const connector = await this.getConnector();
return connector.addAdmin(adminAddress);
}
/**
* Remove an admin from the smart wallet.
* @param adminAddress - The address of the admin to remove.
*/
async removeAdmin(adminAddress) {
const connector = await this.getConnector();
return connector.removeAdmin(adminAddress);
}
/**
* Get all the admins and session keys active on the smart wallet.
*/
async getAllActiveSigners() {
const connector = await this.getConnector();
return connector.getAllActiveSigners();
}
/**
* Get the underlying account contract of the smart wallet.
* @returns The account contract of the smart wallet.
*/
async getAccountContract() {
const connector = await this.getConnector();
return connector.getAccountContract();
}
/**
* Get the underlying account factory contract of the smart wallet.
* @returns The account factory contract.
*/
async getFactoryContract() {
const connector = await this.getConnector();
return connector.getFactoryContract();
}
async verifySignature(message, signature, address, chainId) {
try {
const messageHash = utils.hashMessage(message);
const messageHashBytes = utils.arrayify(messageHash);
const recoveredAddress = utils.recoverAddress(messageHashBytes, signature);
if (recoveredAddress === address) {
return true;
}
} catch {
// no-op
}
// Check if the address is a smart contract wallet
if (chainId !== undefined) {
try {
return await checkContractWalletSignature(message, signature, address, chainId || 1, this.options?.clientId, this.options?.secretKey);
} catch {
// no-op
}
}
return false;
}
autoConnect(params) {
return this.connect(params);
}
/**
* Connect the SmartWallet with given personalWallet
* @param connectOptions -
* The `connectOptions` object includes the following properties:
*
* #### personalWallet
* The instance of a personal wallet that can sign transactions on the Smart Wallet.
* Must be of type `EVMWallet` instance such as `CoinbaseWallet` or `MetamaskWallet`.
*
* @returns A Promise that resolves to the address of the Smart Wallet.
*/
connect(connectOptions) {
return super.connect(connectOptions);
}
}
/**
* @internal
*/
/**
* @internal
*/
_defineProperty(SmartWallet, "meta", {
name: "Smart Wallet",
iconURL: "ipfs://QmeAJVqn17aDNQhjEU3kcWVZCFBrfta8LzaDGkS8Egdiyk/smart-wallet.svg"
});
/**
* @internal
*/
_defineProperty(SmartWallet, "id", walletIds.smartWallet);
export { SmartWallet };