@@ -22,6 +22,8 @@ import { Safe4337Pack, GenericFeeEstimator, PimlicoFeeEstimator } from '@tethert
2222
2323import { ConfigurationError } from './errors.js'
2424
25+ const FEE_TOLERANCE_COEFFICIENT = 120n
26+
2527/** @typedef {import('ethers').Eip1193Provider } Eip1193Provider */
2628
2729/** @typedef {import('@tetherto/wdk-safe-relay-kit').UserOperationReceipt } UserOperationReceipt */
@@ -37,6 +39,13 @@ import { ConfigurationError } from './errors.js'
3739
3840/** @typedef {import('@tetherto/wdk-wallet-evm').TypedData } TypedData */
3941
42+ /**
43+ * @typedef {Object } CachedQuote
44+ * @property {bigint } fee - The estimated fee with tolerance buffer applied.
45+ * @property {number } createdAt - The timestamp when the quote was created.
46+ * @property {string } txKey - A serialized key of the transaction used for cache matching.
47+ */
48+
4049/**
4150 * @typedef {Object } EvmErc4337WalletCommonConfig
4251 * @property {number } chainId - The blockchain's id (e.g., 1 for ethereum).
@@ -114,6 +123,14 @@ export default class WalletAccountReadOnlyEvmErc4337 extends WalletAccountReadOn
114123 */
115124 this . _feeEstimator = undefined
116125
126+ /**
127+ * Cached quote from the last fee estimation.
128+ *
129+ * @protected
130+ * @type {CachedQuote | undefined }
131+ */
132+ this . _lastQuote = undefined
133+
117134 /**
118135 * The chain id.
119136 *
@@ -199,6 +216,9 @@ export default class WalletAccountReadOnlyEvmErc4337 extends WalletAccountReadOn
199216 /**
200217 * Quotes the costs of a send transaction operation.
201218 *
219+ * The result is cached internally for up to 2 minutes. If `sendTransaction` is called with the
220+ * same transaction within that window, the cached fee is reused without an additional RPC round-trip.
221+ *
202222 * @param {EvmTransaction | EvmTransaction[] } tx - The transaction, or an array of multiple transactions to send in batch.
203223 * @param {Partial<EvmErc4337WalletPaymasterTokenConfig | EvmErc4337WalletSponsorshipPolicyConfig | EvmErc4337WalletNativeCoinsConfig> } [config] - If set, overrides the given configuration options.
204224 * @returns {Promise<Omit<TransactionResult, 'hash'>> } The transaction's quotes.
@@ -216,17 +236,24 @@ export default class WalletAccountReadOnlyEvmErc4337 extends WalletAccountReadOn
216236 return { fee : 0n }
217237 }
218238
219- const fee = await this . _getUserOperationGasCost ( [ tx ] . flat ( ) , {
239+ const estimatedFee = await this . _getUserOperationGasCost ( [ tx ] . flat ( ) , {
220240 ...mergedConfig ,
221241 amountToApprove : useNativeCoins ? 0n : BigInt ( Number . MAX_SAFE_INTEGER )
222242 } )
223243
224- return { fee : BigInt ( fee ) }
244+ const fee = BigInt ( estimatedFee ) * FEE_TOLERANCE_COEFFICIENT / 100n
245+
246+ this . _lastQuote = { fee, createdAt : Date . now ( ) , txKey : WalletAccountReadOnlyEvmErc4337 . _getTxKey ( tx ) }
247+
248+ return { fee }
225249 }
226250
227251 /**
228252 * Quotes the costs of a transfer operation.
229253 *
254+ * The result is cached internally for up to 2 minutes. If `transfer` is called with the
255+ * same transaction within that window, the cached fee is reused without an additional RPC round-trip.
256+ *
230257 * @param {TransferOptions } options - The transfer's options.
231258 * @param {Partial<EvmErc4337WalletPaymasterTokenConfig | EvmErc4337WalletSponsorshipPolicyConfig | EvmErc4337WalletNativeCoinsConfig> } [config] - If set, overrides the given configuration options.
232259 * @returns {Promise<Omit<TransferResult, 'hash'>> } The transfer's quotes.
@@ -448,6 +475,17 @@ export default class WalletAccountReadOnlyEvmErc4337 extends WalletAccountReadOn
448475 return this . _feeEstimator
449476 }
450477
478+ /**
479+ * Returns a serialized key for transaction cache matching.
480+ *
481+ * @protected
482+ * @param {EvmTransaction | EvmTransaction[] } tx - The transaction(s) to serialize.
483+ * @returns {string } The serialized transaction key.
484+ */
485+ static _getTxKey ( tx ) {
486+ return JSON . stringify ( [ tx ] . flat ( ) , ( _ , v ) => typeof v === 'bigint' ? v . toString ( ) : v )
487+ }
488+
451489 /** @private */
452490 async _getUserOperationGasCost ( txs , { amountToApprove, ...config } ) {
453491 const safe4337Pack = await this . _getSafe4337Pack ( config )
0 commit comments