UNPKG

@alchemy/aa-core

Version:

viem based SDK that enables interactions with ERC-4337 Smart Accounts. ABIs are based off the definitions generated in @account-abstraction/contracts

253 lines 9.78 kB
import { getContract, http, trim, } from "viem"; import { EntryPointAbi_v6 as EntryPointAbi } from "../abis/EntryPointAbi_v6.js"; import { createBundlerClient, } from "../client/bundlerClient.js"; import { getEntryPoint } from "../entrypoint/index.js"; import { BatchExecutionNotSupportedError, FailedToGetStorageSlotError, GetCounterFactualAddressError, UpgradeToAndCallNotSupportedError, } from "../errors/account.js"; import { InvalidRpcUrlError } from "../errors/client.js"; import { Logger } from "../logger.js"; import { wrapSignatureWith6492 } from "../signer/utils.js"; import { createBaseSmartAccountParamsSchema } from "./schema.js"; export var DeploymentState; (function (DeploymentState) { DeploymentState["UNDEFINED"] = "0x0"; DeploymentState["NOT_DEPLOYED"] = "0x1"; DeploymentState["DEPLOYED"] = "0x2"; })(DeploymentState || (DeploymentState = {})); export class BaseSmartContractAccount { constructor(params_) { Object.defineProperty(this, "factoryAddress", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "deploymentState", { enumerable: true, configurable: true, writable: true, value: DeploymentState.UNDEFINED }); Object.defineProperty(this, "accountAddress", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "accountInitCode", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "signer", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "entryPoint", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "entryPointAddress", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "rpcProvider", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "encodeUpgradeToAndCall", { enumerable: true, configurable: true, writable: true, value: async (_upgradeToImplAddress, _upgradeToInitData) => { throw new UpgradeToAndCallNotSupportedError("BaseAccount"); } }); Object.defineProperty(this, "extend", { enumerable: true, configurable: true, writable: true, value: (fn) => { const extended = fn(this); for (const key in this) { delete extended[key]; } return Object.assign(this, extended); } }); const params = createBaseSmartAccountParamsSchema().parse(params_); this.entryPointAddress = params.entryPointAddress ?? getEntryPoint(params.chain).address; const rpcUrl = typeof params.rpcClient === "string" ? params.rpcClient : params.rpcClient.transport.type === "http" ? params.rpcClient.transport.url || params.chain.rpcUrls.default.http[0] : undefined; const fetchOptions = typeof params.rpcClient === "string" ? undefined : params.rpcClient.transport.type === "http" ? params.rpcClient.transport.fetchOptions : undefined; this.rpcProvider = rpcUrl ? createBundlerClient({ chain: params.chain, transport: http(rpcUrl, { fetchOptions: { ...fetchOptions, headers: { ...fetchOptions?.headers, ...(rpcUrl.toLowerCase().indexOf("alchemy") > -1 ? { "Alchemy-Aa-Sdk-Signer": params.signer?.signerType || "unknown", "Alchemy-Aa-Sdk-Factory-Address": params.factoryAddress, } : undefined), }, }, }), }) : params.rpcClient; this.accountAddress = params.accountAddress; this.factoryAddress = params.factoryAddress; this.signer = params.signer; this.accountInitCode = params.initCode; this.entryPoint = getContract({ address: this.entryPointAddress, abi: EntryPointAbi, client: this.rpcProvider, }); } async signUserOperationHash(uoHash) { return this.signMessage(uoHash); } async signTypedData(_params) { throw new Error("signTypedData not supported"); } async signMessageWith6492(msg) { const [isDeployed, signature] = await Promise.all([ this.isAccountDeployed(), this.signMessage(msg), ]); return this.create6492Signature(isDeployed, signature); } async signTypedDataWith6492(params) { const [isDeployed, signature] = await Promise.all([ this.isAccountDeployed(), this.signTypedData(params), ]); return this.create6492Signature(isDeployed, signature); } async encodeBatchExecute(_txs) { throw new BatchExecutionNotSupportedError("BaseAccount"); } async getNonce() { if (!(await this.isAccountDeployed())) { return 0n; } const address = await this.getAddress(); return this.entryPoint.read.getNonce([address, BigInt(0)]); } async getInitCode() { if (this.deploymentState === DeploymentState.DEPLOYED) { return "0x"; } const contractCode = await this.rpcProvider.getBytecode({ address: await this.getAddress(), }); if ((contractCode?.length ?? 0) > 2) { this.deploymentState = DeploymentState.DEPLOYED; return "0x"; } else { this.deploymentState = DeploymentState.NOT_DEPLOYED; } return this._getAccountInitCode(); } async getAddress() { if (!this.accountAddress) { const initCode = await this._getAccountInitCode(); Logger.verbose("[BaseSmartContractAccount](getAddress) initCode: ", initCode); try { await this.entryPoint.simulate.getSenderAddress([initCode]); } catch (err) { Logger.verbose("[BaseSmartContractAccount](getAddress) getSenderAddress err: ", err); if (err.cause?.data?.errorName === "SenderAddressResult") { this.accountAddress = err.cause.data.args[0]; Logger.verbose("[BaseSmartContractAccount](getAddress) entryPoint.getSenderAddress result:", this.accountAddress); return this.accountAddress; } if (err.details === "Invalid URL") { throw new InvalidRpcUrlError(); } } throw new GetCounterFactualAddressError(); } return this.accountAddress; } getSigner() { return this.signer; } getFactoryAddress() { return this.factoryAddress; } getEntryPointAddress() { return this.entryPointAddress; } async isAccountDeployed() { return (await this.getDeploymentState()) === DeploymentState.DEPLOYED; } async getDeploymentState() { if (this.deploymentState === DeploymentState.UNDEFINED) { const initCode = await this.getInitCode(); return initCode === "0x" ? DeploymentState.DEPLOYED : DeploymentState.NOT_DEPLOYED; } else { return this.deploymentState; } } async parseFactoryAddressFromAccountInitCode() { const initCode = await this._getAccountInitCode(); const factoryAddress = `0x${initCode.substring(2, 42)}`; const factoryCalldata = `0x${initCode.substring(42)}`; return [factoryAddress, factoryCalldata]; } async getImplementationAddress() { const accountAddress = await this.getAddress(); const storage = await this.rpcProvider.getStorageAt({ address: accountAddress, slot: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", }); if (storage == null) { throw new FailedToGetStorageSlotError("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", "Proxy Implementation Address"); } return trim(storage); } async _getAccountInitCode() { return this.accountInitCode ?? this.getAccountInitCode(); } async create6492Signature(isDeployed, signature) { if (isDeployed) { return signature; } const [factoryAddress, factoryCalldata] = await this.parseFactoryAddressFromAccountInitCode(); Logger.verbose(`[BaseSmartContractAccount](create6492Signature)\ factoryAddress: ${factoryAddress}, factoryCalldata: ${factoryCalldata}`); return wrapSignatureWith6492({ factoryAddress, factoryCalldata, signature, }); } } //# sourceMappingURL=base.js.map