@etherspot/modular-sdk
Version:
Etherspot Modular SDK - build with ERC-7579 smart accounts modules
304 lines • 13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseAccountAPI = void 0;
const index_js_1 = require("../common/index.js");
const calcPreVerificationGas_js_1 = require("./calcPreVerificationGas.js");
const index_js_2 = require("../index.js");
const context_js_1 = require("../context.js");
const viem_1 = require("viem");
const abis_js_1 = require("../common/abis.js");
const index_js_3 = require("../common/utils/index.js");
const bignumber_js_1 = require("../types/bignumber.js");
const index_js_4 = require("../wallet/index.js");
const index_js_5 = require("../network/index.js");
class BaseAccountAPI {
constructor(params) {
this.isPhantom = true;
const optionsLike = params.optionsLike;
const { chain, chainId, rpcProviderUrl, factoryWallet, bundlerProvider, } = optionsLike;
this.services = {
networkService: new index_js_2.NetworkService(chainId),
walletService: new index_js_4.WalletService(params.wallet, { provider: rpcProviderUrl, chain }, bundlerProvider.url, chainId),
};
this.context = new context_js_1.Context(this.services);
this.factoryUsed = factoryWallet;
this.overheads = params.overheads;
this.entryPointAddress = params.entryPointAddress;
this.accountAddress = params.accountAddress;
this.factoryAddress = params.factoryAddress;
this.publicClient = params.publicClient;
this.validatorAddress = params.optionsLike?.multipleOwnerECDSAValidatorAddress ?? index_js_5.Networks[params.optionsLike.chainId]?.contracts?.multipleOwnerECDSAValidator ?? index_js_5.DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS;
this.hookMultiplexerAddress = index_js_5.Networks[params.optionsLike.chainId]?.contracts?.hookMultiPlexer || index_js_1.AddressZero;
}
get error$() {
return this.context.error$;
}
get supportedNetworks() {
return this.services.networkService.supportedNetworks;
}
destroy() {
this.context.destroy();
}
async signMessage(dto) {
const { message } = await (0, index_js_2.validateDto)(dto, index_js_2.SignMessageDto);
await this.require({
network: false,
});
const initCode = await this.getInitCode();
return this.services.walletService.signMessage(message, `0x${this.validatorAddress.slice(2)}`, `0x${this.factoryAddress.slice(2)}`, `0x${initCode.substring(42)}`);
}
async setPaymasterApi(paymaster) {
this.paymasterAPI = paymaster;
}
async require(options = {}) {
options = {
network: true,
wallet: true,
...options,
};
}
getNetworkChainId(networkName = null) {
let result;
if (!networkName) {
({ chainId: result } = this.services.networkService);
}
else {
const network = this.supportedNetworks.find(({ name }) => name === networkName);
if (!network) {
throw new index_js_1.Exception('Unsupported network');
}
({ chainId: result } = network);
}
return result;
}
async validateResolveName(options = {}) {
options = {
...options,
};
const { networkService } = this.services;
if (options.network && !networkService.chainId) {
throw new index_js_1.Exception('Unknown network');
}
if (!options.name) {
throw new index_js_1.Exception('Require name');
}
}
async init() {
if ((await this.publicClient.getCode({ address: this.entryPointAddress })) === '0x') {
throw new Error(`entryPoint not deployed at ${this.entryPointAddress}`);
}
await this.getAccountAddress();
return this;
}
async checkAccountPhantom() {
if (!this.isPhantom) {
return this.isPhantom;
}
const accountAddress = await this.getAccountAddress();
const senderAddressCode = await this.publicClient.getCode({ address: accountAddress });
if (senderAddressCode && senderAddressCode.length > 2) {
this.isPhantom = false;
}
return this.isPhantom;
}
async getCounterFactualAddress() {
const initCode = await this.getAccountInitCode();
try {
await this.publicClient.simulateContract({
address: this.entryPointAddress,
abi: (0, viem_1.parseAbi)(abis_js_1.entryPointAbi),
functionName: 'getSenderAddress',
args: [initCode]
});
}
catch (e) {
return e.errorArgs.sender;
}
throw new Error('must handle revert');
}
async getInitCode() {
if (await this.checkAccountPhantom()) {
return await this.getAccountInitCode();
}
return '0x';
}
async getVerificationGasLimit() {
return 100000;
}
async getPreVerificationGas(userOp) {
const p = await (0, index_js_3.resolveProperties)(userOp);
return (0, calcPreVerificationGas_js_1.calcPreVerificationGas)(p, this.overheads);
}
packUserOp(userOp) {
return (0, index_js_1.packUserOp)(userOp, false);
}
async encodeUserOpCallDataAndGasLimit(detailsForUserOp) {
function parseNumber(a) {
if (a == null || a === '')
return null;
return bignumber_js_1.BigNumber.from(a.toString());
}
const value = parseNumber(detailsForUserOp.value) ?? bignumber_js_1.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_js_1.BigNumber.from(35000);
return {
callData,
callGasLimit,
};
}
async getUserOpHash(userOp) {
const op = await (0, index_js_3.resolveProperties)(userOp);
const chainId = await this.publicClient.getChainId();
return (0, index_js_1.getUserOpHash)(op, this.entryPointAddress, chainId);
}
async getAccountAddress() {
if (this.senderAddress == null) {
if (this.accountAddress != null) {
this.senderAddress = this.accountAddress;
}
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({
to: deployerAddress,
data: deployerCallData,
});
return estimatedGas ? estimatedGas : 0;
}
async getFeeData() {
const maxFeePerGasResponse = await this.publicClient.estimateFeesPerGas();
const maxPriorityFeePerGasResponse = await this.publicClient.estimateMaxPriorityFeePerGas();
const maxFeePerGas = maxFeePerGasResponse ? bignumber_js_1.BigNumber.from(maxFeePerGasResponse.maxFeePerGas) : null;
const maxPriorityFeePerGas = maxPriorityFeePerGasResponse ? bignumber_js_1.BigNumber.from(maxPriorityFeePerGasResponse.toString()) : null;
return { maxFeePerGas, maxPriorityFeePerGas };
}
async createUnsignedUserOp(info, key = bignumber_js_1.BigNumber.from(0)) {
const { callData, callGasLimit } = await this.encodeUserOpCallDataAndGasLimit(info);
const factoryData = await this.getInitCode();
const initGas = await this.estimateCreationGas(factoryData);
const verificationGasLimit = bignumber_js_1.BigNumber.from(await this.getVerificationGasLimit()).add(initGas);
let { maxFeePerGas, maxPriorityFeePerGas } = info;
if (maxFeePerGas == null || maxPriorityFeePerGas == null) {
let feeData = {};
try {
feeData = await this.getFeeData();
}
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 ?? undefined;
}
if (maxPriorityFeePerGas == null) {
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
}
}
let partialUserOp;
if (factoryData !== '0x') {
partialUserOp = {
sender: await this.getAccountAddress(),
nonce: await this.getNonce(key),
factory: this.factoryAddress,
factoryData: '0x' + factoryData.substring(42),
callData,
callGasLimit,
verificationGasLimit,
maxFeePerGas,
maxPriorityFeePerGas,
};
}
else {
partialUserOp = {
sender: await this.getAccountAddress(),
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;
if (paymasterData?.result.maxFeePerGas && paymasterData?.result.maxPriorityFeePerGas) {
partialUserOp.maxFeePerGas = paymasterData.result.maxFeePerGas;
partialUserOp.maxPriorityFeePerGas = paymasterData.result.maxPriorityFeePerGas;
}
}
partialUserOp.paymasterData = paymasterData ? paymasterData.result.paymasterData : '0x';
partialUserOp.preVerificationGas = paymasterData ? paymasterData.result.preVerificationGas : this.getPreVerificationGas(partialUserOp);
return {
...partialUserOp,
signature: info.dummySignature ?? '0x',
};
}
async signUserOp(userOp) {
const userOpHash = await this.getUserOpHash(userOp);
const signature = await this.signUserOpHash(userOpHash);
return {
...userOp,
signature,
};
}
async createSignedUserOp(info, key = bignumber_js_1.BigNumber.from(0)) {
return await this.signUserOp(await this.createUnsignedUserOp(info, key));
}
async getUserOpReceipt(userOpHash, timeout = 30000, interval = 5000) {
const endtime = Date.now() + timeout;
while (Date.now() < endtime) {
const response = await this.publicClient.request({
method: 'eth_getUserOperationReceipt',
params: [
userOpHash
]
});
if (response && response.receipt !== undefined) {
return response.receipt.transactionHash;
}
await new Promise((resolve) => setTimeout(resolve, interval));
}
return null;
}
async signTypedData(msg) {
const initCode = await this.getInitCode();
return await this.services.walletService.signTypedData(msg, `0x${this.validatorAddress.slice(2)}`, `0x${this.factoryAddress.slice(2)}`, `0x${initCode.substring(42)}`);
}
}
exports.BaseAccountAPI = BaseAccountAPI;
//# sourceMappingURL=BaseAccountAPI.js.map