UNPKG

@etherspot/remote-signer

Version:

Etherspot Permissioned Signer SDK - signs the UserOp with SessionKey and sends it to the Bundler

764 lines (759 loc) 27 kB
import { isModuleInstalled } from "./chunk-DNBP5Z37.mjs"; import { signUserOpWithSessionKey } from "./chunk-RUG2C3EH.mjs"; import { isAValidSessionKey } from "./chunk-3CLTAXSX.mjs"; import { getOnchainSessionKeyData, isSessionKeyLiveOnChain } from "./chunk-M2QU7VOG.mjs"; import { validateDto } from "./chunk-EPBRUPO6.mjs"; import { SignMessageDto } from "./chunk-EKOEYO6L.mjs"; import { EtherspotBundler } from "./chunk-DHYNFLYW.mjs"; import { HttpRpcClient } from "./chunk-67OVV5YO.mjs"; import { ErrorHandler } from "./chunk-FVCQUX3O.mjs"; import { VerifyingPaymasterAPI } from "./chunk-OYGOAYJB.mjs"; import { calcPreVerificationGas } from "./chunk-APTQT4MR.mjs"; import { getGasFee } from "./chunk-SKQVU7ST.mjs"; import { getUserOpHash, packUserOp } from "./chunk-XNGSGKPY.mjs"; import { getExecuteMode, resolveProperties } from "./chunk-M6FK2HC2.mjs"; import { getPublicClient, getViemAddress } from "./chunk-BHLNGJH5.mjs"; import { DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS, Networks } from "./chunk-NJPIYKQV.mjs"; import { BigNumber } from "./chunk-Q4Z2NTL2.mjs"; import { _makeBootstrapConfig, makeBootstrapConfig } from "./chunk-32NXSZNM.mjs"; import { accountAbi, bootstrapAbi, entryPointAbi, factoryAbi } from "./chunk-KNNHGAPS.mjs"; import { http, isBytes } from "./chunk-EGVR4AKQ.mjs"; import { concat, encodeAbiParameters, encodeFunctionData, formatEther, getAddress, pad, parseAbi, parseAbiItem, parseAbiParameters, toHex } from "./chunk-VPBLFL5G.mjs"; // src/sdk/base/EtherspotWalletAPI.ts var ADDRESS_ZERO = getAddress("0x0000000000000000000000000000000000000000"); var EtherspotWalletAPI = class extends BaseAccountAPI { constructor(params) { super(params); this.index = params.index ?? 0; this.multipleOwnerECDSAValidatorAddress = Networks[params.chainId]?.contracts?.multipleOwnerECDSAValidator ?? DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS; } getEOAAddress() { return this.externalViemAccount.address; } /** * return the value to put into the "initCode" field, if the account is not yet deployed. * this value holds the "factory" address, followed by this account's information */ async getAccountInitCode() { if (this.factoryAddress == null || this.factoryAddress == "") { throw new Error("no factory to get initCode"); } const initCode = await this.getInitCodeData(); const salt = pad(toHex(this.index), { size: 32 }); const functionData = encodeFunctionData({ functionName: "createAccount", abi: parseAbi(factoryAbi), args: [ salt, initCode ] }); return concat([ this.factoryAddress, functionData ]); } async getInitCodeData() { const validators = makeBootstrapConfig(this.multipleOwnerECDSAValidatorAddress, "0x"); const executors = makeBootstrapConfig(ADDRESS_ZERO, "0x"); const hook = _makeBootstrapConfig(ADDRESS_ZERO, "0x"); const fallbacks = makeBootstrapConfig(ADDRESS_ZERO, "0x"); const initMSAData = encodeFunctionData({ functionName: "initMSA", abi: parseAbi(bootstrapAbi), args: [validators, executors, hook, fallbacks] }); const eoaAddress = await this.getEOAAddress(); const initCode = encodeAbiParameters( parseAbiParameters("address, address, bytes"), [eoaAddress, this.bootstrapAddress, initMSAData] ); return initCode; } async getNonce(key = BigNumber.from(0)) { const etherspotWalletAddress = await this.getEtherspotWalletAddress(); const dummyKey = key.eq(0) ? getAddress(this.multipleOwnerECDSAValidatorAddress) + "00000000" : getAddress(key.toHexString()) + "00000000"; const nonceResponse = await this.publicClient.readContract({ address: this.entryPointAddress, abi: parseAbi(entryPointAbi), functionName: "getNonce", args: [etherspotWalletAddress, BigInt(dummyKey)] }); return nonceResponse; } /** * encode a method call from entryPoint to our contract * @param target * @param value * @param data */ async encodeExecute(target, value, data) { const executeMode = getExecuteMode({ callType: "0x00" /* SINGLE */, execType: "0x00" /* DEFAULT */ }); let valueToProcess; if (BigNumber.isBigNumber(value)) { valueToProcess = value.toString(); } else if (isBytes(value)) { valueToProcess = new Uint8Array(value); } else { if (typeof value === "object" && value !== null && "length" in value) { valueToProcess = new Uint8Array(Object.values(value)); } else { valueToProcess = value; } } const calldata = concat([ target, pad(toHex(valueToProcess), { size: 32 }), data ]); return encodeFunctionData({ functionName: "execute", abi: parseAbi(accountAbi), args: [executeMode, calldata] }); } async encodeBatch(targets, values, datas) { const executeMode = getExecuteMode({ callType: "0x01" /* BATCH */, execType: "0x00" /* DEFAULT */ }); const result = targets.map((target, index) => ({ target, value: values[index], callData: datas[index] })); const convertedResult = result.map((item) => ({ ...item, // Convert `value` from BigNumberish to bigint value: typeof item.value === "bigint" ? item.value : BigInt(item.value.toString()) })); const calldata = encodeAbiParameters( parseAbiParameters("(address target,uint256 value,bytes callData)[]"), [convertedResult] ); return encodeFunctionData({ functionName: "execute", abi: parseAbi(accountAbi), args: [executeMode, calldata] }); } }; // src/sdk/sdk.ts var RemoteSignerSdk = class _RemoteSignerSdk { constructor(externalViemAccount, sdkOptions) { this.userOpsBatch = { to: [], data: [], value: [] }; const { index, etherspotWalletAddress, chainId, apiKey, sessionKey, rpcProviderUrl } = sdkOptions; if (!externalViemAccount) throw new Error("EOAAddress - ViemAccount object is required"); this.externalViemAccount = externalViemAccount; if (!etherspotWalletAddress) throw new Error("etherspotWalletAddress is required"); this.etherspotWalletAddress = etherspotWalletAddress; if (!chainId || chainId <= 0) throw new Error("chainId is required"); if (!Networks[chainId]) { throw new Error("ChainId not found in the Networks"); } this.chainId = chainId; this.index = index ?? 0; if (!apiKey) throw new Error("apiKey is required"); this.apiKey = apiKey; if (!sessionKey) throw new Error("sessionKey is required"); this.sessionKey = sessionKey; if (!sdkOptions.bundlerProvider) { sdkOptions.bundlerProvider = new EtherspotBundler(chainId); } this.factoryUsed = sdkOptions.factoryWallet ?? "etherspot" /* ETHERSPOT */; let viemClientUrl = ""; if (rpcProviderUrl) { viemClientUrl = rpcProviderUrl; } else { viemClientUrl = sdkOptions.bundlerProvider.url; } this.providerUrl = viemClientUrl; this.publicClient = getPublicClient({ chainId, transport: http(viemClientUrl) }); let entryPointAddress = Networks[chainId].contracts.entryPoint; if (Networks[chainId].contracts.walletFactory == "") throw new Error("The selected factory is not deployed in the selected chain_id"); let walletFactoryAddress = Networks[chainId].contracts.walletFactory; if (sdkOptions.entryPointAddress) entryPointAddress = sdkOptions.entryPointAddress; if (sdkOptions.walletFactoryAddress) walletFactoryAddress = sdkOptions.walletFactoryAddress; if (entryPointAddress == "") throw new Error("entryPointAddress not set on the given chain_id"); if (walletFactoryAddress == "") throw new Error("walletFactoryAddress not set on the given chain_id"); this.etherspotWallet = new EtherspotWalletAPI({ sdkOptions, entryPointAddress, factoryAddress: walletFactoryAddress, etherspotWalletAddress, externalViemAccount: this.externalViemAccount, publicClient: this.publicClient, index: this.index, chainId: this.chainId }); this.bundler = new HttpRpcClient(sdkOptions.bundlerProvider.url, entryPointAddress, chainId, this.publicClient); } async initialize(sdkOptions) { const isPhantom = await this.etherspotWallet.checkAccountPhantom(); if (isPhantom) { throw new Error(`EtherspotWallet: ${this.etherspotWalletAddress} is not deployed/initialized on chain: ${this.chainId}`); } const erc20SessionKeyValidator = await this.getERC20SessionKeyValidator(); const isModuleInstalledIndicator = await isModuleInstalled( this.publicClient, this.etherspotWalletAddress, "0x01" /* VALIDATOR */, erc20SessionKeyValidator ); if (!isModuleInstalledIndicator) { throw new Error(`Module: ${erc20SessionKeyValidator} is not installed on etherspotWalletAddress: ${this.etherspotWalletAddress} on chainId: ${Networks[this.chainId].chain.name}`); } const sessionKeyExists = await isAValidSessionKey(this.publicClient, erc20SessionKeyValidator, this.etherspotWalletAddress, this.chainId, this.apiKey, this.sessionKey); if (!sessionKeyExists) { throw new Error(`Sessionkey: ${sdkOptions.sessionKey} is invalid for etherspotWalletAddress: ${this.etherspotWalletAddress} and apiKey: ${this.apiKey} on chainId: ${Networks[this.chainId].chain.name}`); } } async getERC20SessionKeyValidator() { if (this.erc20SessionKeyValidator) { return this.erc20SessionKeyValidator; } this.erc20SessionKeyValidator = Networks[this.chainId]?.contracts?.erc20SessionKeyValidator; if (!this.erc20SessionKeyValidator) { throw new Error("erc20SessionKeyValidator not found in the Networks for chainId: `" + this.chainId + "`"); } return this.erc20SessionKeyValidator; } static async create(externalViemAccount, sdkOptions) { const instance = new _RemoteSignerSdk(externalViemAccount, sdkOptions); await instance.initialize(sdkOptions); return instance; } getPublicClient() { return this.publicClient; } getProviderUrl() { return this.providerUrl; } async validateSessionKey() { const sessionKeyValidator = await this.getERC20SessionKeyValidator(); return isAValidSessionKey( this.publicClient, sessionKeyValidator, this.etherspotWalletAddress, this.chainId, this.apiKey, this.sessionKey ); } async isSessionKeyLiveOnChain() { const sessionKeyValidator = await this.getERC20SessionKeyValidator(); return isSessionKeyLiveOnChain( this.etherspotWalletAddress, this.publicClient, sessionKeyValidator, this.sessionKey ); } async getSessionKeyOnChainData() { const sessionKeyValidator = await this.getERC20SessionKeyValidator(); return getOnchainSessionKeyData( this.etherspotWalletAddress, this.publicClient, sessionKeyValidator, this.sessionKey ); } async signUserOp(userOp) { if (!this.etherspotWallet.etherspotWalletAddress) { throw new ErrorHandler("EtherspotWalletAddress not found", 500); } const sessionSignedUserOp = await signUserOpWithSessionKey( this.etherspotWallet.etherspotWalletAddress, this.chainId, this.apiKey, this.sessionKey, userOp ); return sessionSignedUserOp; } async getCounterFactualAddress() { return this.etherspotWallet.getEtherspotWalletAddress(); } async estimate(params = { nonceKey: BigNumber.from(0) }) { const { paymasterDetails, gasDetails, callGasLimit, nonceKey } = params; const dummySignature = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; if (this.userOpsBatch.to.length < 1) { throw new ErrorHandler("cannot sign empty transaction batch", 1); } if (paymasterDetails?.url) { const paymasterAPI = new VerifyingPaymasterAPI(paymasterDetails.url, this.etherspotWallet.entryPointAddress, paymasterDetails.context ?? {}); this.etherspotWallet.setPaymasterApi(paymasterAPI); } else this.etherspotWallet.setPaymasterApi(null); const tx = { target: this.userOpsBatch.to, values: this.userOpsBatch.value, data: this.userOpsBatch.data, dummySignature, ...gasDetails }; const gasInfo = await this.getGasFee(); const partialtx = await this.etherspotWallet.createUnsignedUserOp({ ...tx, maxFeePerGas: gasInfo.maxFeePerGas, maxPriorityFeePerGas: gasInfo.maxPriorityFeePerGas }, nonceKey); if (callGasLimit) { partialtx.callGasLimit = BigNumber.from(callGasLimit).toHexString(); } const bundlerGasEstimate = await this.bundler.getVerificationGasInfo(partialtx); if (gasDetails?.maxFeePerGas && gasDetails?.maxPriorityFeePerGas) { partialtx.maxFeePerGas = gasDetails.maxFeePerGas; partialtx.maxPriorityFeePerGas = gasDetails.maxPriorityFeePerGas; } else if (bundlerGasEstimate.maxFeePerGas && bundlerGasEstimate.maxPriorityFeePerGas) { partialtx.maxFeePerGas = bundlerGasEstimate.maxFeePerGas; partialtx.maxPriorityFeePerGas = bundlerGasEstimate.maxPriorityFeePerGas; } else { const gas = await this.getGasFee(); partialtx.maxFeePerGas = gas.maxFeePerGas; partialtx.maxPriorityFeePerGas = gas.maxPriorityFeePerGas; } if (bundlerGasEstimate.preVerificationGas) { partialtx.preVerificationGas = BigNumber.from(bundlerGasEstimate.preVerificationGas); partialtx.verificationGasLimit = BigNumber.from(bundlerGasEstimate.verificationGasLimit ?? bundlerGasEstimate.verificationGas); const expectedCallGasLimit = BigNumber.from(bundlerGasEstimate.callGasLimit); if (!callGasLimit) partialtx.callGasLimit = expectedCallGasLimit; else if (BigNumber.from(callGasLimit).lt(expectedCallGasLimit)) throw new ErrorHandler(`CallGasLimit is too low. Expected atleast ${expectedCallGasLimit.toString()}`); } return partialtx; } async send(signedUserOp) { return this.bundler.sendUserOpToBundler(signedUserOp); } async getGasFee() { const version = await this.bundler.getBundlerVersion(); if (version && version.includes("skandha")) return this.bundler.getSkandhaGasPrice(); return getGasFee(this.publicClient); } async getNativeBalance() { const balance = await this.publicClient.getBalance({ address: getViemAddress(this.etherspotWallet.accountAddress) }); return formatEther(balance); } async getUserOpReceipt(userOpHash) { return await this.etherspotWallet.getUserOpReceipt(userOpHash); } async getUserOpHash(userOp) { return this.etherspotWallet.getUserOpHash(userOp); } async addUserOpsToBatch(tx) { if (!tx.data && !tx.value) throw new ErrorHandler("Data and Value both cannot be empty", 1); this.userOpsBatch.to.push(tx.to); this.userOpsBatch.value.push(tx.value ?? BigNumber.from(0)); this.userOpsBatch.data.push(tx.data ?? "0x"); return this.userOpsBatch; } async clearUserOpsFromBatch() { this.userOpsBatch.to = []; this.userOpsBatch.data = []; this.userOpsBatch.value = []; } async totalGasEstimated(userOp) { const callGasLimit = BigNumber.from(await userOp.callGasLimit); const verificationGasLimit = BigNumber.from(await userOp.verificationGasLimit); const preVerificationGas = BigNumber.from(await userOp.preVerificationGas); return callGasLimit.add(verificationGasLimit).add(preVerificationGas); } }; // src/sdk/index.ts var sdk_default = RemoteSignerSdk; // src/sdk/base/BaseAccountAPI.ts var BaseAccountAPI = class { /** * base constructor. * subclass SHOULD add parameters that define the owner (signer) of this wallet */ constructor(params) { this.isPhantom = true; const sdkOptions = params.sdkOptions; const { chainId, // rpcProviderUrl, factoryWallet, bundlerProvider } = sdkOptions; this.factoryUsed = factoryWallet; this.overheads = params.overheads; this.entryPointAddress = params.entryPointAddress; this.externalViemAccount = params.externalViemAccount; this.etherspotWalletAddress = params.etherspotWalletAddress; this.factoryAddress = params.factoryAddress; this.walletClient = params.walletClient; this.publicClient = params.publicClient; } // sdk // wallet /** * signs message * @param dto * @return Promise<string> */ async signMessage(dto) { const { message } = await validateDto(dto, SignMessageDto); await this.require({ network: false }); return this.walletClient.signMessage( { account: this.externalViemAccount, message } ); } async setPaymasterApi(paymaster) { this.paymasterAPI = paymaster; } // private async require(options = {}) { options = { network: true, wallet: true, ...options }; } async init() { if (await this.publicClient.getCode({ address: this.entryPointAddress }) === "0x") { throw new Error(`entryPoint not deployed at ${this.entryPointAddress}`); } await this.getEtherspotWalletAddress(); return this; } /** * check if the contract is already deployed. */ async checkAccountPhantom() { if (!this.isPhantom) { return this.isPhantom; } const etherspotWalletAddress = await this.getEtherspotWalletAddress(); const senderAddressCode = await this.publicClient.getCode({ address: etherspotWalletAddress }); if (!senderAddressCode || senderAddressCode === "0x" || senderAddressCode.length <= 2) { this.isPhantom = true; } else { this.isPhantom = false; } return this.isPhantom; } /** * calculate the account address even before it is deployed */ async getCounterFactualAddress() { const initCode = await this.getAccountInitCode(); try { await this.publicClient.simulateContract({ address: this.entryPointAddress, abi: parseAbi(entryPointAbi), functionName: "getSenderAddress", args: [initCode] }); } catch (e) { return e.errorArgs.sender; } throw new Error("must handle revert"); } /** * return initCode value to into the UserOp. * (either deployment code, or empty hex if contract already deployed) */ async getInitCode() { if (await this.checkAccountPhantom()) { return await this.getAccountInitCode(); } return "0x"; } /** * return maximum gas used for verification. * NOTE: createUnsignedUserOp will add to this value the cost of creation, if the contract is not yet created. */ async getVerificationGasLimit() { return 1e5; } /** * should cover cost of putting calldata on-chain, and some overhead. * actual overhead depends on the expected bundle size */ async getPreVerificationGas(userOp) { const p = await resolveProperties(userOp); return calcPreVerificationGas(p, this.overheads); } /** * ABI-encode a user operation. used for calldata cost estimation */ packUserOp(userOp) { return packUserOp(userOp, false); } async encodeUserOpCallDataAndGasLimit(detailsForUserOp) { function parseNumber(a) { if (a == null || a === "") return null; return BigNumber.from(a.toString()); } const value = parseNumber(detailsForUserOp.value) ?? BigNumber.from(0); let callData; const data = detailsForUserOp.data; let target = detailsForUserOp.target; if (typeof data === "string") { if (typeof target !== "string") { throw new Error("must have target address if data is single value"); } callData = await this.encodeExecute(target, value, data); } else { if (typeof target === "string") { target = Array(data.length).fill(target); } callData = await this.encodeBatch(target, detailsForUserOp.values, data); } const callGasLimit = parseNumber(detailsForUserOp.gasLimit) ?? BigNumber.from(35e3); return { callData, callGasLimit }; } /** * return userOpHash for signing. * This value matches entryPoint.getUserOpHash (calculated off-chain, to avoid a view call) * @param userOp userOperation, (signature field ignored) */ async getUserOpHash(userOp) { const op = await resolveProperties(userOp); const chainId = await this.publicClient.getChainId(); return getUserOpHash(op, this.entryPointAddress, chainId); } /** * return the account's address. * this value is valid even before deploying the contract. */ async getEtherspotWalletAddress() { if (this.senderAddress == null) { if (this.etherspotWalletAddress != null) { this.senderAddress = this.etherspotWalletAddress; } else { this.senderAddress = await this.getCounterFactualAddress(); } } return this.senderAddress; } async estimateCreationGas(initCode) { if (initCode == null || initCode === "0x") return 0; const deployerAddress = initCode.substring(0, 42); const deployerCallData = "0x" + initCode.substring(42); const estimatedGas = await this.publicClient.estimateGas({ account: this.externalViemAccount, to: deployerAddress, data: deployerCallData }); return estimatedGas ? estimatedGas : 0; } async getViemFeeData() { const block = await this.publicClient.getBlock(); const gasPrice = await this.publicClient.getGasPrice(); const gasPriceInDecimals = BigNumber.from(gasPrice); let lastBaseFeePerGas = null, maxFeePerGas = null, maxPriorityFeePerGas = null; if (block && block.baseFeePerGas) { lastBaseFeePerGas = block.baseFeePerGas; const baseFeePerGasAsBigNumber = BigNumber.from(block.baseFeePerGas); maxPriorityFeePerGas = BigNumber.from("1500000000"); maxFeePerGas = baseFeePerGasAsBigNumber.mul(2).add(maxPriorityFeePerGas); } return { lastBaseFeePerGas, maxFeePerGas, maxPriorityFeePerGas, gasPrice: gasPriceInDecimals }; } /** * create a UserOperation, filling all details (except signature) * - if account is not yet created, add initCode to deploy it. * - if gas or nonce are missing, read them from the chain (note that we can't fill gaslimit before the account is created) * @param info */ async createUnsignedUserOp(info, key = BigNumber.from(0)) { const { callData, callGasLimit } = await this.encodeUserOpCallDataAndGasLimit(info); const factoryData = await this.getInitCode(); const initGas = await this.estimateCreationGas(factoryData); const verificationGasLimit = BigNumber.from(await this.getVerificationGasLimit()).add(initGas); let { maxFeePerGas, maxPriorityFeePerGas } = info; if (maxFeePerGas == null || maxPriorityFeePerGas == null) { let feeData = {}; try { feeData = await this.getViemFeeData(); } catch (err) { console.warn( "getGas: eth_maxPriorityFeePerGas failed, falling back to legacy gas price." ); const gas = await this.publicClient.getGasPrice(); feeData = { maxFeePerGas: gas, maxPriorityFeePerGas: gas }; } if (maxFeePerGas == null) { maxFeePerGas = feeData.maxFeePerGas ?? void 0; } if (maxPriorityFeePerGas == null) { maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? void 0; } } let partialUserOp; if (factoryData !== "0x") { partialUserOp = { sender: await this.getEtherspotWalletAddress(), nonce: await this.getNonce(key), factory: this.factoryAddress, factoryData: "0x" + factoryData.substring(42), callData, callGasLimit, verificationGasLimit, maxFeePerGas, maxPriorityFeePerGas }; } else { partialUserOp = { sender: await this.getEtherspotWalletAddress(), nonce: await this.getNonce(key), factoryData: "0x" + factoryData.substring(42), callData, callGasLimit, verificationGasLimit, maxFeePerGas, maxPriorityFeePerGas }; } let paymasterData = null; if (this.paymasterAPI != null) { const userOpForPm = { ...partialUserOp, preVerificationGas: this.getPreVerificationGas(partialUserOp) }; paymasterData = await this.paymasterAPI.getPaymasterData(userOpForPm); partialUserOp.verificationGasLimit = paymasterData.result.verificationGasLimit; partialUserOp.preVerificationGas = paymasterData.result.preVerificationGas; partialUserOp.callGasLimit = paymasterData.result.callGasLimit; partialUserOp.paymaster = paymasterData.result.paymaster; partialUserOp.paymasterVerificationGasLimit = paymasterData.result.paymasterVerificationGasLimit; partialUserOp.paymasterPostOpGasLimit = paymasterData.result.paymasterPostOpGasLimit; } partialUserOp.paymasterData = paymasterData ? paymasterData.result.paymasterData : "0x"; return { ...partialUserOp, preVerificationGas: this.getPreVerificationGas(partialUserOp), signature: info.dummySignature ?? "0x" }; } /** * get the transaction that has this userOpHash mined, or null if not found * @param userOpHash returned by sendUserOpToBundler (or by getUserOpHash..) * @param timeout stop waiting after this timeout * @param interval time to wait between polls. * @return the transactionHash this userOp was mined, or null if not found. */ async getUserOpReceipt(userOpHash, timeout = 3e4, interval = 5e3) { const endtime = Date.now() + timeout; while (Date.now() < endtime) { const filter = await this.publicClient.createEventFilter({ address: this.entryPointAddress, args: { userOpHash }, event: parseAbiItem("event UserOperationEvent(bytes32 indexed userOpHash,address indexed sender,address indexed paymaster,uint256 nonce,bool success,uint256 actualGasCost,uint256 actualGasUsed)") }); const logs = await this.publicClient.getFilterLogs({ filter }); if (logs && logs.length > 0) { return logs[0].transactionHash; } await new Promise((resolve) => setTimeout(resolve, interval)); } return null; } // TODO fix signTypedData async signTypedData(domain, types, message) { const typesObject = {}; types.forEach((type) => { if (!typesObject[type.type]) { typesObject[type.type] = [type]; } else { typesObject[type.type].push(type); } }); return await this.walletClient.signTypedData({ domain, types: typesObject, primaryType: "UserOperation", account: this.externalViemAccount, message }); } }; export { BaseAccountAPI, EtherspotWalletAPI, RemoteSignerSdk, sdk_default }; //# sourceMappingURL=chunk-HQVIQWLZ.mjs.map