diff --git a/CHANGELOG.md b/CHANGELOG.md index 791e950b..7200e9a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +### New +- Added ability to override callDataLimit on estimate step by the user + ## [1.3.13] - 2023-11-22 ### Bug Fixes - Removed UnsupportedChainId error and now can add custom network details diff --git a/examples/20-callDataLimit.ts b/examples/20-callDataLimit.ts new file mode 100644 index 00000000..477cb1cc --- /dev/null +++ b/examples/20-callDataLimit.ts @@ -0,0 +1,56 @@ +import { ethers } from 'ethers'; +import { PrimeSdk } from '../src'; +import { printOp } from '../src/sdk/common/OperationUtils'; +import * as dotenv from 'dotenv'; +import { sleep } from '../src/sdk/common'; + +dotenv.config(); + +const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address +const value = '0.0001'; // transfer value + +async function main() { + // initializating sdk... + const primeSdk = new PrimeSdk({ privateKey: process.env.WALLET_PRIVATE_KEY }, { chainId: Number(process.env.CHAIN_ID), projectKey: 'public-prime-testnet-key' }) + + console.log('address: ', primeSdk.state.walletAddress) + + // get address of EtherspotWallet... + const address: string = await primeSdk.getCounterFactualAddress(); + console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`); + + // clear the transaction batch + await primeSdk.clearUserOpsFromBatch(); + + // add transactions to the batch + const transactionBatch = await primeSdk.addUserOpsToBatch({to: recipient, value: ethers.utils.parseEther(value)}); + console.log('transactions: ', transactionBatch); + + // get balance of the account address + const balance = await primeSdk.getNativeBalance(); + + console.log('balances: ', balance); + + // estimate transactions added to the batch and get the fee data for the UserOp + // passing callGasLimit as 40000 to manually set it + const op = await primeSdk.estimate(null, null, 40000); + console.log(`Estimate UserOp: ${await printOp(op)}`); + + // sign the UserOp and sending to the bundler... + const uoHash = await primeSdk.send(op); + console.log(`UserOpHash: ${uoHash}`); + + // get transaction hash... + console.log('Waiting for transaction...'); + let userOpsReceipt = null; + const timeout = Date.now() + 60000; // 1 minute timeout + while((userOpsReceipt == null) && (Date.now() < timeout)) { + await sleep(2); + userOpsReceipt = await primeSdk.getUserOpReceipt(uoHash); + } + console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt); +} + +main() + .catch(console.error) + .finally(() => process.exit()); diff --git a/package-lock.json b/package-lock.json index d9c02e79..359955c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@etherspot/prime-sdk", - "version": "1.3.13", + "version": "1.3.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@etherspot/prime-sdk", - "version": "1.3.13", + "version": "1.3.14", "license": "MIT", "dependencies": { "@apollo/client": "3.8.6", diff --git a/package.json b/package.json index 5be55e01..9b241221 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@etherspot/prime-sdk", - "version": "1.3.13", + "version": "1.3.14", "description": "Etherspot Prime (Account Abstraction) SDK", "keywords": [ "ether", diff --git a/src/sdk/base/HttpRpcClient.ts b/src/sdk/base/HttpRpcClient.ts index 46d9df47..c0650ee3 100644 --- a/src/sdk/base/HttpRpcClient.ts +++ b/src/sdk/base/HttpRpcClient.ts @@ -98,13 +98,21 @@ export class HttpRpcClient { } async getBundlerVersion(): Promise { - const version = await this.userOpJsonRpcProvider.send('web3_clientVersion', []); - return version; + try { + const version = await this.userOpJsonRpcProvider.send('web3_clientVersion', []); + return version; + } catch (err) { + return null; + } } async getUserOpsReceipt(uoHash: string): Promise { - const response = await this.userOpJsonRpcProvider.send('eth_getUserOperationReceipt', [uoHash]); - return response; + try { + const response = await this.userOpJsonRpcProvider.send('eth_getUserOperationReceipt', [uoHash]); + return response; + } catch (err) { + return null; + } } private async printUserOperation( diff --git a/src/sdk/common/getGasFee.ts b/src/sdk/common/getGasFee.ts index e1bd0840..8ff6796b 100644 --- a/src/sdk/common/getGasFee.ts +++ b/src/sdk/common/getGasFee.ts @@ -9,7 +9,9 @@ export interface Gas { export async function getGasFee(provider: ethers.providers.JsonRpcProvider): Promise { try { const [fee, block] = await provider.send('eth_maxPriorityFeePerGas', []); - if (BigNumber.from(0).eq(fee)) return { maxFeePerGas: BigNumber.from(1), maxPriorityFeePerGas: BigNumber.from(1) }; + if (BigNumber.from(0).eq(fee)) { + throw new Error('failed to get priorityFeePerGas'); + } const tip = ethers.BigNumber.from(fee); const buffer = tip.div(100).mul(bufferPercent); const maxPriorityFeePerGas = tip.add(buffer); diff --git a/src/sdk/sdk.ts b/src/sdk/sdk.ts index 76f56408..888afddf 100644 --- a/src/sdk/sdk.ts +++ b/src/sdk/sdk.ts @@ -10,7 +10,7 @@ import { import { Factory, PaymasterApi, SdkOptions } from './interfaces'; import { Network } from "./network"; import { BatchUserOpsRequest, Exception, getGasFee, onRampApiKey, openUrl, UserOpsRequest } from "./common"; -import { BigNumber, ethers, providers } from 'ethers'; +import { BigNumber, BigNumberish, ethers, providers } from 'ethers'; import { getNetworkConfig, Networks, onRamperAllNetworks } from './network/constants'; import { UserOperationStruct } from './contracts/account-abstraction/contracts/core/BaseAccount'; import { EtherspotWalletAPI, HttpRpcClient, VerifyingPaymasterAPI } from './base'; @@ -169,7 +169,7 @@ export class PrimeSdk { return this.etherspotWallet.getCounterFactualAddress(); } - async estimate(paymasterDetails?: PaymasterApi, gasDetails?: TransactionGasInfoForUserOp) { + async estimate(paymasterDetails?: PaymasterApi, gasDetails?: TransactionGasInfoForUserOp, callDataLimit?: BigNumberish) { if (this.userOpsBatch.to.length < 1) { throw new ErrorHandler('cannot sign empty transaction batch', 1); } @@ -186,12 +186,20 @@ export class PrimeSdk { ...gasDetails, } + const gasInfo = await this.getGasFee() + const partialtx = await this.etherspotWallet.createUnsignedUserOp({ ...tx, - maxFeePerGas: 1, - maxPriorityFeePerGas: 1, + maxFeePerGas: gasInfo.maxFeePerGas, + maxPriorityFeePerGas: gasInfo.maxPriorityFeePerGas, }); + if (callDataLimit) { + partialtx.callGasLimit = BigNumber.from(callDataLimit).toHexString(); + } + + partialtx.signature = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; + /** * Dummy signature used only in the case of zeroDev factory contract */ @@ -219,7 +227,11 @@ export class PrimeSdk { if (bundlerGasEstimate.preVerificationGas) { partialtx.preVerificationGas = BigNumber.from(bundlerGasEstimate.preVerificationGas); partialtx.verificationGasLimit = BigNumber.from(bundlerGasEstimate.verificationGasLimit ?? bundlerGasEstimate.verificationGas); - partialtx.callGasLimit = BigNumber.from(bundlerGasEstimate.callGasLimit); + const expectedCallGasLimit = BigNumber.from(bundlerGasEstimate.callGasLimit); + if (!callDataLimit) + partialtx.callGasLimit = expectedCallGasLimit; + else if (BigNumber.from(callDataLimit).lt(expectedCallGasLimit)) + throw new ErrorHandler(`CallGasLimit is too low. Expected atleast ${expectedCallGasLimit.toString()}`); } return partialtx; @@ -228,7 +240,7 @@ export class PrimeSdk { async getGasFee() { const version = await this.bundler.getBundlerVersion(); - if (version.includes('skandha')) + if (version && version.includes('skandha')) return this.bundler.getSkandhaGasPrice(); return getGasFee(this.etherspotWallet.provider as providers.JsonRpcProvider); }