@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
942 lines (929 loc) • 23 kB
JavaScript
import { providers, Contract, utils } from 'ethers';
import { s as setAnalyticsHeaders } from './headers-733a8199.browser.esm.js';
import { i as isTwUrl } from './url-a45219bd.browser.esm.js';
import { chainIdToThirdwebRpc } from '../evm/wallets/abstract/dist/thirdweb-dev-wallets-evm-wallets-abstract.browser.esm.js';
import { isContractDeployed, getChainProvider, ThirdwebSDK } from '@thirdweb-dev/sdk';
import { EntryPoint__factory } from '@account-abstraction/contracts';
const EIP1271_ABI = ["function isValidSignature(bytes32 _hash, bytes _signature) public view returns (bytes4)"];
const EIP1271_MAGICVALUE = "0x1626ba7e";
async function checkContractWalletSignature(message, signature, address, chainId, clientId, secretKey) {
// TODO: remove below `skipFetchSetup` logic when ethers.js v6 support arrives
let _skipFetchSetup = false;
if (typeof globalThis !== "undefined" && "TW_SKIP_FETCH_SETUP" in globalThis && typeof globalThis.TW_SKIP_FETCH_SETUP === "boolean") {
_skipFetchSetup = globalThis.TW_SKIP_FETCH_SETUP;
}
const rpcUrl = chainIdToThirdwebRpc(chainId, clientId);
const headers = {};
if (isTwUrl(rpcUrl)) {
const bundleId = typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis ? globalThis.APP_BUNDLE_ID : undefined;
if (secretKey) {
headers["x-secret-key"] = secretKey;
} else if (clientId) {
headers["x-client-id"] = clientId;
if (bundleId) {
headers["x-bundle-id"] = bundleId;
}
}
// Dashboard token
if (typeof globalThis !== "undefined" && "TW_AUTH_TOKEN" in globalThis && typeof globalThis.TW_AUTH_TOKEN === "string") {
headers["authorization"] = `Bearer ${globalThis.TW_AUTH_TOKEN}`;
}
// CLI token
if (typeof globalThis !== "undefined" && "TW_CLI_AUTH_TOKEN" in globalThis && typeof globalThis.TW_CLI_AUTH_TOKEN === "string") {
headers["authorization"] = `Bearer ${globalThis.TW_CLI_AUTH_TOKEN}`;
headers["x-authorize-wallet"] = "true";
}
setAnalyticsHeaders(headers);
}
const provider = new providers.StaticJsonRpcProvider({
url: rpcUrl,
skipFetchSetup: _skipFetchSetup,
headers
}, chainId);
const walletContract = new Contract(address, EIP1271_ABI, provider);
try {
const res = await walletContract.isValidSignature(utils.hashMessage(message), signature);
return res === EIP1271_MAGICVALUE;
} catch {
return false;
}
}
const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; // v0.6
const ERC6551_REGISTRY = "0x02101dfB77FDE026414827Fdc604ddAF224F0921";
const MANAGED_ACCOUNT_GAS_BUFFER = 50000;
const DEFAULT_FACTORY_ADDRESS = "0x85e23b94e7F5E9cC1fF78BCe78cfb15B81f0DF00";
const ACCOUNT_CORE_ABI = [{
type: "constructor",
inputs: [{
name: "_entrypoint",
type: "address",
internalType: "contract IEntryPoint"
}, {
name: "_factory",
type: "address",
internalType: "address"
}],
stateMutability: "nonpayable"
}, {
type: "receive",
stateMutability: "payable"
}, {
type: "function",
name: "addDeposit",
inputs: [],
outputs: [],
stateMutability: "payable"
}, {
type: "function",
name: "contractURI",
inputs: [],
outputs: [{
name: "",
type: "string",
internalType: "string"
}],
stateMutability: "view"
}, {
type: "function",
name: "entryPoint",
inputs: [],
outputs: [{
name: "",
type: "address",
internalType: "contract IEntryPoint"
}],
stateMutability: "view"
}, {
type: "function",
name: "execute",
inputs: [{
name: "_target",
type: "address",
internalType: "address"
}, {
name: "_value",
type: "uint256",
internalType: "uint256"
}, {
name: "_calldata",
type: "bytes",
internalType: "bytes"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "executeBatch",
inputs: [{
name: "_target",
type: "address[]",
internalType: "address[]"
}, {
name: "_value",
type: "uint256[]",
internalType: "uint256[]"
}, {
name: "_calldata",
type: "bytes[]",
internalType: "bytes[]"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "factory",
inputs: [],
outputs: [{
name: "",
type: "address",
internalType: "address"
}],
stateMutability: "view"
}, {
type: "function",
name: "getAllActiveSigners",
inputs: [],
outputs: [{
name: "signers",
type: "tuple[]",
internalType: "struct IAccountPermissions.SignerPermissions[]",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "startTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "endTimestamp",
type: "uint128",
internalType: "uint128"
}]
}],
stateMutability: "view"
}, {
type: "function",
name: "getAllAdmins",
inputs: [],
outputs: [{
name: "",
type: "address[]",
internalType: "address[]"
}],
stateMutability: "view"
}, {
type: "function",
name: "getAllSigners",
inputs: [],
outputs: [{
name: "signers",
type: "tuple[]",
internalType: "struct IAccountPermissions.SignerPermissions[]",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "startTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "endTimestamp",
type: "uint128",
internalType: "uint128"
}]
}],
stateMutability: "view"
}, {
type: "function",
name: "getMessageHash",
inputs: [{
name: "_hash",
type: "bytes32",
internalType: "bytes32"
}],
outputs: [{
name: "",
type: "bytes32",
internalType: "bytes32"
}],
stateMutability: "view"
}, {
type: "function",
name: "getNonce",
inputs: [],
outputs: [{
name: "",
type: "uint256",
internalType: "uint256"
}],
stateMutability: "view"
}, {
type: "function",
name: "getPermissionsForSigner",
inputs: [{
name: "signer",
type: "address",
internalType: "address"
}],
outputs: [{
name: "",
type: "tuple",
internalType: "struct IAccountPermissions.SignerPermissions",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "startTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "endTimestamp",
type: "uint128",
internalType: "uint128"
}]
}],
stateMutability: "view"
}, {
type: "function",
name: "initialize",
inputs: [{
name: "_defaultAdmin",
type: "address",
internalType: "address"
}, {
name: "_data",
type: "bytes",
internalType: "bytes"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "isActiveSigner",
inputs: [{
name: "signer",
type: "address",
internalType: "address"
}],
outputs: [{
name: "",
type: "bool",
internalType: "bool"
}],
stateMutability: "view"
}, {
type: "function",
name: "isAdmin",
inputs: [{
name: "_account",
type: "address",
internalType: "address"
}],
outputs: [{
name: "",
type: "bool",
internalType: "bool"
}],
stateMutability: "view"
}, {
type: "function",
name: "isValidSignature",
inputs: [{
name: "_hash",
type: "bytes32",
internalType: "bytes32"
}, {
name: "_signature",
type: "bytes",
internalType: "bytes"
}],
outputs: [{
name: "magicValue",
type: "bytes4",
internalType: "bytes4"
}],
stateMutability: "view"
}, {
type: "function",
name: "isValidSigner",
inputs: [{
name: "_signer",
type: "address",
internalType: "address"
}, {
name: "_userOp",
type: "tuple",
internalType: "struct UserOperation",
components: [{
name: "sender",
type: "address",
internalType: "address"
}, {
name: "nonce",
type: "uint256",
internalType: "uint256"
}, {
name: "initCode",
type: "bytes",
internalType: "bytes"
}, {
name: "callData",
type: "bytes",
internalType: "bytes"
}, {
name: "callGasLimit",
type: "uint256",
internalType: "uint256"
}, {
name: "verificationGasLimit",
type: "uint256",
internalType: "uint256"
}, {
name: "preVerificationGas",
type: "uint256",
internalType: "uint256"
}, {
name: "maxFeePerGas",
type: "uint256",
internalType: "uint256"
}, {
name: "maxPriorityFeePerGas",
type: "uint256",
internalType: "uint256"
}, {
name: "paymasterAndData",
type: "bytes",
internalType: "bytes"
}, {
name: "signature",
type: "bytes",
internalType: "bytes"
}]
}],
outputs: [{
name: "",
type: "bool",
internalType: "bool"
}],
stateMutability: "view"
}, {
type: "function",
name: "multicall",
inputs: [{
name: "data",
type: "bytes[]",
internalType: "bytes[]"
}],
outputs: [{
name: "results",
type: "bytes[]",
internalType: "bytes[]"
}],
stateMutability: "nonpayable"
}, {
type: "function",
name: "onERC1155BatchReceived",
inputs: [{
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "uint256[]",
internalType: "uint256[]"
}, {
name: "",
type: "uint256[]",
internalType: "uint256[]"
}, {
name: "",
type: "bytes",
internalType: "bytes"
}],
outputs: [{
name: "",
type: "bytes4",
internalType: "bytes4"
}],
stateMutability: "nonpayable"
}, {
type: "function",
name: "onERC1155Received",
inputs: [{
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "uint256",
internalType: "uint256"
}, {
name: "",
type: "uint256",
internalType: "uint256"
}, {
name: "",
type: "bytes",
internalType: "bytes"
}],
outputs: [{
name: "",
type: "bytes4",
internalType: "bytes4"
}],
stateMutability: "nonpayable"
}, {
type: "function",
name: "onERC721Received",
inputs: [{
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "address",
internalType: "address"
}, {
name: "",
type: "uint256",
internalType: "uint256"
}, {
name: "",
type: "bytes",
internalType: "bytes"
}],
outputs: [{
name: "",
type: "bytes4",
internalType: "bytes4"
}],
stateMutability: "nonpayable"
}, {
type: "function",
name: "setContractURI",
inputs: [{
name: "_uri",
type: "string",
internalType: "string"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "setEntrypointOverride",
inputs: [{
name: "_entrypointOverride",
type: "address",
internalType: "contract IEntryPoint"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "setPermissionsForSigner",
inputs: [{
name: "_req",
type: "tuple",
internalType: "struct IAccountPermissions.SignerPermissionRequest",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "isAdmin",
type: "uint8",
internalType: "uint8"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "permissionStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "permissionEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "uid",
type: "bytes32",
internalType: "bytes32"
}]
}, {
name: "_signature",
type: "bytes",
internalType: "bytes"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "function",
name: "supportsInterface",
inputs: [{
name: "interfaceId",
type: "bytes4",
internalType: "bytes4"
}],
outputs: [{
name: "",
type: "bool",
internalType: "bool"
}],
stateMutability: "view"
}, {
type: "function",
name: "validateUserOp",
inputs: [{
name: "userOp",
type: "tuple",
internalType: "struct UserOperation",
components: [{
name: "sender",
type: "address",
internalType: "address"
}, {
name: "nonce",
type: "uint256",
internalType: "uint256"
}, {
name: "initCode",
type: "bytes",
internalType: "bytes"
}, {
name: "callData",
type: "bytes",
internalType: "bytes"
}, {
name: "callGasLimit",
type: "uint256",
internalType: "uint256"
}, {
name: "verificationGasLimit",
type: "uint256",
internalType: "uint256"
}, {
name: "preVerificationGas",
type: "uint256",
internalType: "uint256"
}, {
name: "maxFeePerGas",
type: "uint256",
internalType: "uint256"
}, {
name: "maxPriorityFeePerGas",
type: "uint256",
internalType: "uint256"
}, {
name: "paymasterAndData",
type: "bytes",
internalType: "bytes"
}, {
name: "signature",
type: "bytes",
internalType: "bytes"
}]
}, {
name: "userOpHash",
type: "bytes32",
internalType: "bytes32"
}, {
name: "missingAccountFunds",
type: "uint256",
internalType: "uint256"
}],
outputs: [{
name: "validationData",
type: "uint256",
internalType: "uint256"
}],
stateMutability: "nonpayable"
}, {
type: "function",
name: "verifySignerPermissionRequest",
inputs: [{
name: "req",
type: "tuple",
internalType: "struct IAccountPermissions.SignerPermissionRequest",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "isAdmin",
type: "uint8",
internalType: "uint8"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "permissionStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "permissionEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "uid",
type: "bytes32",
internalType: "bytes32"
}]
}, {
name: "signature",
type: "bytes",
internalType: "bytes"
}],
outputs: [{
name: "success",
type: "bool",
internalType: "bool"
}, {
name: "signer",
type: "address",
internalType: "address"
}],
stateMutability: "view"
}, {
type: "function",
name: "withdrawDepositTo",
inputs: [{
name: "withdrawAddress",
type: "address",
internalType: "address payable"
}, {
name: "amount",
type: "uint256",
internalType: "uint256"
}],
outputs: [],
stateMutability: "nonpayable"
}, {
type: "event",
name: "AdminUpdated",
inputs: [{
name: "signer",
type: "address",
indexed: true,
internalType: "address"
}, {
name: "isAdmin",
type: "bool",
indexed: false,
internalType: "bool"
}],
anonymous: false
}, {
type: "event",
name: "ContractURIUpdated",
inputs: [{
name: "prevURI",
type: "string",
indexed: false,
internalType: "string"
}, {
name: "newURI",
type: "string",
indexed: false,
internalType: "string"
}],
anonymous: false
}, {
type: "event",
name: "Initialized",
inputs: [{
name: "version",
type: "uint8",
indexed: false,
internalType: "uint8"
}],
anonymous: false
}, {
type: "event",
name: "SignerPermissionsUpdated",
inputs: [{
name: "authorizingSigner",
type: "address",
indexed: true,
internalType: "address"
}, {
name: "targetSigner",
type: "address",
indexed: true,
internalType: "address"
}, {
name: "permissions",
type: "tuple",
indexed: false,
internalType: "struct IAccountPermissions.SignerPermissionRequest",
components: [{
name: "signer",
type: "address",
internalType: "address"
}, {
name: "isAdmin",
type: "uint8",
internalType: "uint8"
}, {
name: "approvedTargets",
type: "address[]",
internalType: "address[]"
}, {
name: "nativeTokenLimitPerTransaction",
type: "uint256",
internalType: "uint256"
}, {
name: "permissionStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "permissionEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityStartTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "reqValidityEndTimestamp",
type: "uint128",
internalType: "uint128"
}, {
name: "uid",
type: "bytes32",
internalType: "bytes32"
}]
}],
anonymous: false
}];
const sdkCache = new Map();
function getSDK(chain) {
const cached = sdkCache.get(chain);
if (cached) {
return cached;
}
const sdk = new ThirdwebSDK(chain);
sdkCache.set(chain, sdk);
return sdk;
}
/**
* Get all the signers added to the given smart wallet (excluding owner)
* @param chain - The chain to use
* @param factoryAddress - The factory address
* @param smartWalletAddress - The smart wallet address
* @returns The list of signers
*/
async function getAllSigners(chain, factoryAddress, smartWalletAddress) {
const readOnlySDK = getSDK(chain);
const factoryContract = await readOnlySDK.getContract(factoryAddress);
const signers = await factoryContract.call("getSignersOfAccount", [smartWalletAddress]);
return signers;
}
/**
* Get all the smart wallets associated with a personal wallet address
* @param chain - The chain to use
* @param factoryAddress - The factory address
* @param personalWalletAddress - The personal wallet address
* @returns The list of smart wallets
*/
async function getAllSmartWallets(chain, factoryAddress, personalWalletAddress) {
const readOnlySDK = getSDK(chain);
const factoryContract = await readOnlySDK.getContract(factoryAddress);
const ownedAccount = await getSmartWalletAddress(chain, factoryAddress, personalWalletAddress);
const accessibleAccounts = await factoryContract.call("getAccountsOfSigner", [personalWalletAddress]);
return {
owned: ownedAccount,
hasSignerRole: accessibleAccounts
};
}
/**
* Check if a smart wallet is deployed for a given personal wallet address
* @param chain - The chain to use
* @param factoryAddress - The factory address
* @param personalWalletAddress - The personal wallet address
* @returns True if the smart wallet is deployed
*/
async function isSmartWalletDeployed(chain, factoryAddress, personalWalletAddress) {
let data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "0x";
const readOnlySDK = getSDK(chain);
const factoryContract = await readOnlySDK.getContract(factoryAddress);
const accountAddress = await factoryContract.call("getAddress", [personalWalletAddress, data]);
const isDeployed = await isContractDeployed(accountAddress, readOnlySDK.getProvider());
return isDeployed;
}
async function isZkSyncChain(network, clientId, secretKey) {
const provider = getChainProvider(network, {
clientId,
secretKey
});
const chainId = (await provider.getNetwork()).chainId;
return chainId === 324 || chainId === 300 || chainId === 302;
}
/**
* Get the associated smart wallet address for a given personal wallet address
* @param chain - The chain to use
* @param factoryAddress - The factory address
* @param personalWalletAddress - The personal wallet address
* @returns The smart wallet address
*/
async function getSmartWalletAddress(chain, factoryAddress, personalWalletAddress) {
let data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "0x";
const readOnlySDK = getSDK(chain);
const factoryContract = await readOnlySDK.getContract(factoryAddress);
const accountAddress = await factoryContract.call("getAddress", [personalWalletAddress, data]);
return accountAddress;
}
async function getUserOpReceipt(chain, userOpHash) {
let timeout = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 30000;
let interval = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2000;
let entryPointAddress = arguments.length > 4 ? arguments[4] : undefined;
const readOnlySDK = getSDK(chain);
const entrypoint = await readOnlySDK.getContract(entryPointAddress || ENTRYPOINT_ADDRESS, EntryPoint__factory.abi);
// do a first check for the last 5000 blocks
const pastEvents = await entrypoint.events.getEvents("UserOperationEvent", {
fromBlock: -9000,
// look at the last 9000 blocks
filters: {
userOpHash
}
});
if (pastEvents[0]) {
return pastEvents[0].transaction.transactionHash;
}
// if not found, query the last 100 blocks every 2 seconds for the next 30 seconds
const endtime = Date.now() + timeout;
while (Date.now() < endtime) {
const events = await entrypoint.events.getEvents("UserOperationEvent", {
fromBlock: -100,
filters: {
userOpHash
}
});
if (events[0]) {
return events[0].transaction.transactionHash;
}
await new Promise(resolve => setTimeout(resolve, interval));
}
return null;
}
export { ACCOUNT_CORE_ABI as A, DEFAULT_FACTORY_ADDRESS as D, ERC6551_REGISTRY as E, MANAGED_ACCOUNT_GAS_BUFFER as M, getAllSmartWallets as a, getSmartWalletAddress as b, checkContractWalletSignature as c, getUserOpReceipt as d, isZkSyncChain as e, ENTRYPOINT_ADDRESS as f, getAllSigners as g, isSmartWalletDeployed as i };