From 0969a115ea76deef0fee63e77189cc22c0fbd181 Mon Sep 17 00:00:00 2001 From: franciscotobar <100875069+franciscotobar@users.noreply.github.com> Date: Wed, 17 Jul 2024 06:11:47 -0600 Subject: [PATCH] fix/payment-gas-estimation (#91) * feat(utils): estimatePaymentGas * refactor(utils): estimation values * refactor(RelayClient): isCustom handler * refactor(rRlayClient): method name and test --- package.json | 2 +- src/RelayClient.ts | 49 +++--- src/common/relayClient.types.ts | 14 ++ src/common/relayHub.types.ts | 1 + src/gasEstimator/gasEstimator.ts | 4 +- src/utils.ts | 89 +++++++++- test/RelayClient.test.ts | 84 ++++++---- test/gasEstimator/gasEstimator.test.ts | 2 +- test/utils.test.ts | 224 ++++++++++++++++++++++++- 9 files changed, 403 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index 3affcac..7d208c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rsksmart/rif-relay-client", - "version": "2.2.0", + "version": "2.2.1", "private": false, "description": "This project contains all the client code for the rif relay system.", "license": "MIT", diff --git a/src/RelayClient.ts b/src/RelayClient.ts index 2b77ceb..8d0814f 100644 --- a/src/RelayClient.ts +++ b/src/RelayClient.ts @@ -59,8 +59,9 @@ import EnvelopingEventEmitter, { import { standardMaxPossibleGasEstimation } from './gasEstimator'; import { estimateInternalCallGas, - estimateTokenTransferGas, + estimatePaymentGas, getSmartWalletAddress, + isDataEmpty, maxPossibleGasVerification, validateRelayResponse, } from './utils'; @@ -92,7 +93,8 @@ class RelayClient extends EnvelopingEventEmitter { } private _getEnvelopingRequestDetails = async ( - envelopingRequest: UserDefinedEnvelopingRequest + envelopingRequest: UserDefinedEnvelopingRequest, + isCustom = false ): Promise => { const isDeployment: boolean = isDeployRequest( envelopingRequest as EnvelopingRequest @@ -132,7 +134,7 @@ class RelayClient extends EnvelopingEventEmitter { const tokenAmount = (await envelopingRequest.request.tokenAmount) ?? constants.Zero; - if (this._isContractCallInvalid(to, data, value)) { + if (!isCustom && this._isCallInvalid(to, data, value)) { throw new Error('Contract execution needs data or value to be sent.'); } @@ -264,14 +266,14 @@ class RelayClient extends EnvelopingEventEmitter { return completeRequest; }; - private _isContractCallInvalid( + private _isCallInvalid( to: string, data: BytesLike, value: BigNumberish ): boolean { return ( to != constants.AddressZero && - data === '0x00' && + isDataEmpty(data.toString()) && BigNumber.from(value).isZero() ); } @@ -324,6 +326,7 @@ class RelayClient extends EnvelopingEventEmitter { relayHubAddress: await relayHub, signature: await accountManager.sign(updatedRelayRequest, signerWallet), relayMaxNonce, + isCustom: options?.isCustom, }; const httpRequest: EnvelopingTxRequest = { relayRequest: updatedRelayRequest, @@ -350,8 +353,8 @@ class RelayClient extends EnvelopingEventEmitter { isCustom?: boolean ): Promise { const { - request: { tokenGas, tokenAmount, tokenContract }, - relayData: { gasPrice, callForwarder }, + request: { tokenGas, tokenAmount }, + relayData: { callForwarder }, } = envelopingRequest; const currentTokenAmount = BigNumber.from(tokenAmount); @@ -387,25 +390,16 @@ class RelayClient extends EnvelopingEventEmitter { }) : await callForwarder; - const isNativePayment = (await tokenContract) === constants.AddressZero; - - return isNativePayment - ? await estimateInternalCallGas({ - from: origin, - to, - gasPrice, - data, - }) - : await estimateTokenTransferGas({ - relayRequest: { - ...envelopingRequest, - relayData: { - ...envelopingRequest.relayData, - feesReceiver, - }, - }, - preDeploySWAddress: origin, - }); + return await estimatePaymentGas({ + relayRequest: { + ...envelopingRequest, + relayData: { + ...envelopingRequest.relayData, + feesReceiver, + }, + }, + preDeploySWAddress: origin, + }); } private async _calculateGasPrice(): Promise { @@ -491,7 +485,8 @@ class RelayClient extends EnvelopingEventEmitter { options?: RelayTxOptions ): Promise { const envelopingRequestDetails = await this._getEnvelopingRequestDetails( - envelopingRequest + envelopingRequest, + options?.isCustom ); //FIXME we should implement the relay selection strategy diff --git a/src/common/relayClient.types.ts b/src/common/relayClient.types.ts index b062622..0af613a 100644 --- a/src/common/relayClient.types.ts +++ b/src/common/relayClient.types.ts @@ -21,7 +21,19 @@ type RequestConfig = { estimatedGasCorrectionFactor?: BigNumberish; }; +type PaymentGasEstimationParams = Pick & + Pick< + RequestConfig, + | 'isSmartWalletDeploy' + | 'preDeploySWAddress' + | 'internalEstimationCorrection' + | 'estimatedGasCorrectionFactor' + >; + //FIXME name standardization +/** + * @deprecated The type was replaced by {@link PaymentGasEstimationParams} + */ type TokenGasEstimationParams = Pick & Pick< RequestConfig, @@ -36,6 +48,7 @@ type EstimateInternalGasParams = Pick< RelayRequestBody, 'data' | 'to' | 'from' > & + Partial> & Pick & Pick< RequestConfig, @@ -67,6 +80,7 @@ type SmartWalletAddressTxOptions = { export type { RequestConfig, + PaymentGasEstimationParams, TokenGasEstimationParams, EstimateInternalGasParams, HubEnvelopingTx, diff --git a/src/common/relayHub.types.ts b/src/common/relayHub.types.ts index ee167e1..c6a686b 100644 --- a/src/common/relayHub.types.ts +++ b/src/common/relayHub.types.ts @@ -30,6 +30,7 @@ type EnvelopingMetadata = { relayHubAddress: RelayRequestBody['relayHub']; relayMaxNonce: number; signature: string; + isCustom?: boolean; }; export type { HubInfo, EnvelopingMetadata, RelayInfo, RelayManagerData }; diff --git a/src/gasEstimator/gasEstimator.ts b/src/gasEstimator/gasEstimator.ts index d159caf..594eb80 100644 --- a/src/gasEstimator/gasEstimator.ts +++ b/src/gasEstimator/gasEstimator.ts @@ -1,5 +1,5 @@ import { BigNumber, utils } from 'ethers'; -import { getSmartWalletAddress, estimateTokenTransferGas } from '../utils'; +import { getSmartWalletAddress, estimatePaymentGas } from '../utils'; import { isDeployRequest } from '../common/relayRequest.utils'; import type { EnvelopingTxRequest } from '../common/relayTransaction.types'; import { @@ -39,7 +39,7 @@ const estimateRelayMaxPossibleGas = async ( }) : undefined; - const tokenEstimation = await estimateTokenTransferGas({ + const tokenEstimation = await estimatePaymentGas({ relayRequest: { ...relayRequest, request: { diff --git a/src/utils.ts b/src/utils.ts index 30a763f..a17159d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,6 +29,7 @@ import type { EnvelopingTxRequest, DeployRequest, RelayRequest, + PaymentGasEstimationParams, } from './common'; import { MISSING_SMART_WALLET_ADDRESS, @@ -38,9 +39,10 @@ import { import RelayClient from './RelayClient'; import type { HttpClient } from './api/common'; -const INTERNAL_TRANSACTION_ESTIMATED_CORRECTION = 20000; // When estimating the gas an internal call is going to spend, we need to substract some gas inherent to send the parameters to the blockchain +const INTERNAL_TRANSACTION_ESTIMATED_CORRECTION = 18500; // When estimating the gas an internal call is going to spend, we need to substract some gas inherent to send the parameters to the blockchain +const INTERNAL_TRANSACTION_NATIVE_ESTIMATED_CORRECTION = 10500; const ESTIMATED_GAS_CORRECTION_FACTOR = 1; -const SHA3_NULL_S = utils.keccak256('0x'); +const SHA3_NULL_S = utils.keccak256('0x00'); const FACTOR = 0.25; const GAS_VERIFICATION_ATTEMPTS = 4; @@ -61,6 +63,13 @@ const estimateInternalCallGas = async ({ let estimation: BigNumber = await provider.estimateGas(estimateGasParams); + const data = await estimateGasParams.data; + + if (isDataEmpty(data.toString())) { + internalEstimationCorrection = + INTERNAL_TRANSACTION_NATIVE_ESTIMATED_CORRECTION; + } + estimation = applyInternalEstimationCorrection( estimation, internalEstimationCorrection @@ -69,6 +78,73 @@ const estimateInternalCallGas = async ({ return applyGasCorrectionFactor(estimation, estimatedGasCorrectionFactor); }; +const estimatePaymentGas = async ({ + internalEstimationCorrection, + estimatedGasCorrectionFactor, + preDeploySWAddress, + relayRequest, +}: PaymentGasEstimationParams): Promise => { + const { + request: { tokenContract, tokenAmount, to }, + relayData: { callForwarder, gasPrice, feesReceiver }, + } = relayRequest; + + if (tokenAmount.toString() === '0') { + return constants.Zero; + } + + let tokenOrigin: PromiseOrValue | undefined; + + if (isDeployRequest(relayRequest)) { + tokenOrigin = preDeploySWAddress; + + // If it is a deploy and tokenGas was not defined, then the smartwallet address + // is required to estimate the token gas. This value should be calculated prior to + // the call to this function + if (!tokenOrigin || tokenOrigin === constants.AddressZero) { + throw Error(MISSING_SMART_WALLET_ADDRESS); + } + } else { + tokenOrigin = callForwarder; + + if (tokenOrigin === constants.AddressZero) { + throw Error(MISSING_CALL_FORWARDER); + } + } + + const isNativePayment = (await tokenContract) === constants.AddressZero; + + if (isNativePayment) { + return await estimateInternalCallGas({ + from: tokenOrigin, + to, + gasPrice, + value: tokenAmount, + data: '0x', + }); + } + + const provider = getProvider(); + const erc20 = IERC20__factory.connect(await tokenContract, provider); + const gasCost = await erc20.estimateGas.transfer(feesReceiver, tokenAmount, { + from: tokenOrigin, + gasPrice, + }); + + const internalCallCost = applyInternalEstimationCorrection( + gasCost, + internalEstimationCorrection + ); + + return applyGasCorrectionFactor( + internalCallCost, + estimatedGasCorrectionFactor + ); +}; + +/** + * @deprecated The method was replaced by {@link estimatePaymentGas} + */ const estimateTokenTransferGas = async ({ internalEstimationCorrection, estimatedGasCorrectionFactor, @@ -142,7 +218,7 @@ const getSmartWalletAddress = async ({ const recovererAddress = recoverer ?? constants.AddressZero; - const logicParamsHash = data ?? SHA3_NULL_S; + const logicParamsHash = data ? utils.keccak256(data) : SHA3_NULL_S; const logic = to ?? constants.AddressZero; @@ -362,17 +438,24 @@ const applyFactor = (value: BigNumberish, factor: number) => { ); }; +const isDataEmpty = (data: string) => { + return ['', '0x', '0x00'].includes(data); +}; + export { estimateInternalCallGas, + estimatePaymentGas, estimateTokenTransferGas, getSmartWalletAddress, applyGasCorrectionFactor, applyInternalEstimationCorrection, INTERNAL_TRANSACTION_ESTIMATED_CORRECTION, + INTERNAL_TRANSACTION_NATIVE_ESTIMATED_CORRECTION, ESTIMATED_GAS_CORRECTION_FACTOR, SHA3_NULL_S, validateRelayResponse, useEnveloping, getRelayClientGenerator, maxPossibleGasVerification, + isDataEmpty, }; diff --git a/test/RelayClient.test.ts b/test/RelayClient.test.ts index fb24154..7a83862 100644 --- a/test/RelayClient.test.ts +++ b/test/RelayClient.test.ts @@ -29,7 +29,7 @@ import type { TransactionResponse, TransactionReceipt, } from '@ethersproject/providers'; -import { solidityKeccak256 } from 'ethers/lib/utils'; +import { BytesLike, solidityKeccak256 } from 'ethers/lib/utils'; import { SinonStub, SinonStubbedInstance, createSandbox } from 'sinon'; import sinonChai from 'sinon-chai'; import { HttpClient } from '../src/api/common'; @@ -185,6 +185,11 @@ describe('RelayClient', function () { maxPossibleGas: BigNumber ) => Promise; _getRelayServer: () => Promise; + _isCallInvalid: ( + to: string, + data: BytesLike, + value: BigNumberish + ) => boolean; } & { [key in keyof RelayClient]: RelayClient[key]; }; @@ -229,7 +234,7 @@ describe('RelayClient', function () { beforeEach(function () { fakeTokenGasEstimation = constants.Two; sandbox - .stub(relayUtils, 'estimateTokenTransferGas') + .stub(relayUtils, 'estimatePaymentGas') .returns(Promise.resolve(fakeTokenGasEstimation)); accountManagerStub = { sign: sandbox @@ -439,7 +444,7 @@ describe('RelayClient', function () { expect(tokenGas).to.be.eql(constants.Zero); }); - it('should return given tokenGas if its request', async function () { + it('should return given tokenGas if it is in the request', async function () { const request = { ...FAKE_RELAY_REQUEST }; const tokenGas = await relayClient._prepareTokenGas( FAKE_HUB_INFO.feesReceiver, @@ -455,36 +460,13 @@ describe('RelayClient', function () { it('should call getSmartWalletAddress in deploy request', async function () { const addressStub = sandbox.stub(relayUtils, 'getSmartWalletAddress'); - const estimationStub = sandbox.stub( - relayUtils, - 'estimateTokenTransferGas' - ); - - await relayClient._prepareTokenGas(FAKE_HUB_INFO.feesReceiver, { - ...FAKE_DEPLOY_REQUEST, - request: { - ...FAKE_DEPLOY_REQUEST.request, - tokenGas: constants.Zero, - }, - }); - - expect(addressStub).to.be.calledOnce; - expect(estimationStub).to.be.calledOnce; - }); - - it('should call estimateInternalCallGas in deploy request with native token', async function () { - const addressStub = sandbox.stub(relayUtils, 'getSmartWalletAddress'); - const estimationStub = sandbox.stub( - relayUtils, - 'estimateInternalCallGas' - ); + const estimationStub = sandbox.stub(relayUtils, 'estimatePaymentGas'); await relayClient._prepareTokenGas(FAKE_HUB_INFO.feesReceiver, { ...FAKE_DEPLOY_REQUEST, request: { ...FAKE_DEPLOY_REQUEST.request, tokenGas: constants.Zero, - tokenContract: constants.AddressZero, }, }); @@ -495,9 +477,7 @@ describe('RelayClient', function () { it('should return expected value', async function () { sandbox.stub(relayUtils, 'getSmartWalletAddress'); const expectedValue = constants.Two; - sandbox - .stub(relayUtils, 'estimateTokenTransferGas') - .resolves(expectedValue); + sandbox.stub(relayUtils, 'estimatePaymentGas').resolves(expectedValue); const tokenGas = await relayClient._prepareTokenGas( FAKE_HUB_INFO.feesReceiver, @@ -1885,6 +1865,50 @@ describe('RelayClient', function () { }); }); }); + + describe('_isCallInvalid', function () { + let to: string; + let data: BytesLike; + let value: BigNumberish; + + beforeEach(function () { + to = createRandomAddress(); + data = '0x00'; + value = constants.Zero; + }); + + it('should return true if the call is valid', function () { + const result = relayClient._isCallInvalid(to, data, value); + + expect(result).to.be.true; + }); + + it('should return false if address is zero', function () { + const result = relayClient._isCallInvalid( + constants.AddressZero, + data, + value + ); + + expect(result).to.be.false; + }); + + it('should return false if data is not empty', function () { + const result = relayClient._isCallInvalid( + to, + 'FAKE_REQUEST_DATA', + value + ); + + expect(result).to.be.false; + }); + + it('should return false if value is not zero', function () { + const result = relayClient._isCallInvalid(to, data, constants.Two); + + expect(result).to.be.false; + }); + }); }); describe('properties', function () { diff --git a/test/gasEstimator/gasEstimator.test.ts b/test/gasEstimator/gasEstimator.test.ts index f5cf806..49b4e89 100644 --- a/test/gasEstimator/gasEstimator.test.ts +++ b/test/gasEstimator/gasEstimator.test.ts @@ -54,7 +54,7 @@ describe('GasEstimator', function () { fakeFitRelayEstimation = randomBigNumber(10000); relayWorker = Wallet.createRandom().address; sandbox - .stub(relayUtils, 'estimateTokenTransferGas') + .stub(relayUtils, 'estimatePaymentGas') .returns(Promise.resolve(fakeTokenGas)); sandbox .stub(relayUtils, 'getSmartWalletAddress') diff --git a/test/utils.test.ts b/test/utils.test.ts index 1e7a935..98f743e 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -30,6 +30,8 @@ import { validateRelayResponse, getRelayClientGenerator, maxPossibleGasVerification, + estimatePaymentGas, + INTERNAL_TRANSACTION_NATIVE_ESTIMATED_CORRECTION, } from '../src/utils'; import { FAKE_ENVELOPING_CONFIG } from './config.fakes'; import { @@ -40,7 +42,10 @@ import { FAKE_REQUEST_CONFIG, } from './request.fakes'; import * as clientConfiguration from '../src/common/clientConfigurator'; -import type { TokenGasEstimationParams } from '../src/common'; +import type { + PaymentGasEstimationParams, + TokenGasEstimationParams, +} from '../src/common'; import { MISSING_SMART_WALLET_ADDRESS, MISSING_CALL_FORWARDER, @@ -160,6 +165,221 @@ describe('utils', function () { }); }); + describe('estimatePaymentGas', function () { + let ierc20TransferStub: SinonStub; + + beforeEach(function () { + ierc20TransferStub = sinon.stub(); + const estimateGas = { + transfer: ierc20TransferStub, + }; + + const ierc20Stub = { + estimateGas, + } as unknown as sinon.SinonStubbedInstance; + + sinon.stub(IERC20__factory, 'connect').returns(ierc20Stub); + }); + + it('should return native token estimation if token contract is zero address', async function () { + const FAKE_GAS_COST = 30000; + const EXPECTED_ESTIMATION = + FAKE_GAS_COST - INTERNAL_TRANSACTION_NATIVE_ESTIMATED_CORRECTION; + + const providerStub: providers.Provider = sinon.createStubInstance( + providers.Provider + ); + providerStub.estimateGas = sinon.stub().resolves(FAKE_GAS_COST); + sinon.stub(clientConfiguration, 'getProvider').returns(providerStub); + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + request: { + ...FAKE_RELAY_REQUEST.request, + tokenContract: constants.AddressZero, + }, + }, + ...FAKE_REQUEST_CONFIG, + }; + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + + it('should return token estimation if token contract is not zero address', async function () { + const FAKE_GAS_COST = 30000; + const EXPECTED_ESTIMATION = + FAKE_GAS_COST - INTERNAL_TRANSACTION_ESTIMATED_CORRECTION; + + ierc20TransferStub.resolves(BigNumber.from(FAKE_GAS_COST)); + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + request: { + ...FAKE_RELAY_REQUEST.request, + }, + }, + ...FAKE_REQUEST_CONFIG, + }; + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + + it('should return 0 if token amount is 0', async function () { + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + request: { + ...FAKE_RELAY_REQUEST.request, + tokenAmount: constants.Zero, + }, + }, + ...FAKE_REQUEST_CONFIG, + }; + + expect((await estimateTokenTransferGas(request)).toString()).to.be.equal( + constants.Zero.toString() + ); + }); + + it('should fail if it is a deploy and the smartWallet is missing', async function () { + const request: PaymentGasEstimationParams = { + relayRequest: { ...FAKE_DEPLOY_REQUEST }, + preDeploySWAddress: undefined, + }; + + await expect(estimatePaymentGas(request)).to.be.rejectedWith( + MISSING_SMART_WALLET_ADDRESS + ); + }); + + it('should fail if it is a deploy and the smartWallet is the zero address', async function () { + const request: PaymentGasEstimationParams = { + relayRequest: { ...FAKE_DEPLOY_REQUEST }, + preDeploySWAddress: constants.AddressZero, + }; + + await expect(estimatePaymentGas(request)).to.be.rejectedWith( + MISSING_SMART_WALLET_ADDRESS + ); + }); + + it('should fail if it is a relay transaction and the callForwarder is missing', async function () { + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + relayData: { + ...FAKE_RELAY_REQUEST.relayData, + callForwarder: constants.AddressZero, + }, + }, + preDeploySWAddress: constants.AddressZero, + }; + + await expect(estimatePaymentGas(request)).to.be.rejectedWith( + MISSING_CALL_FORWARDER + ); + }); + + it('should correct the value of the estimation when the gas cost is greater than the correction', async function () { + const FAKE_GAS_COST = 30000; + const INTERNAL_CORRECTION = 20000; + const EXPECTED_ESTIMATION = FAKE_GAS_COST - INTERNAL_CORRECTION; + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + relayData: { + ...FAKE_RELAY_REQUEST.relayData, + gasPrice: FAKE_GAS_COST, + }, + }, + internalEstimationCorrection: INTERNAL_CORRECTION, + }; + + ierc20TransferStub.resolves(BigNumber.from(FAKE_GAS_COST)); + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + + it('should not correct the value of the estimation when the gas cost is lower than the correction', async function () { + const FAKE_GAS_COST = 10000; + const INTERNAL_CORRECTION = 20000; + const EXPECTED_ESTIMATION = FAKE_GAS_COST; + + ierc20TransferStub.resolves(BigNumber.from(FAKE_GAS_COST)); + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + relayData: { + ...FAKE_RELAY_REQUEST.relayData, + gasPrice: FAKE_GAS_COST, + }, + }, + internalEstimationCorrection: INTERNAL_CORRECTION, + }; + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + + it('should apply the correction factor', async function () { + const FAKE_GAS_COST = 10000; + const INTERNAL_CORRECTION = 20000; + const CORRECTION_FACTOR = 1.5; + const EXPECTED_ESTIMATION = FAKE_GAS_COST * CORRECTION_FACTOR; + + ierc20TransferStub.resolves(BigNumber.from(FAKE_GAS_COST)); + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + relayData: { + ...FAKE_RELAY_REQUEST.relayData, + gasPrice: FAKE_GAS_COST, + }, + }, + internalEstimationCorrection: INTERNAL_CORRECTION, + estimatedGasCorrectionFactor: CORRECTION_FACTOR, + }; + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + + it('should use by-default values when not sent as parameters', async function () { + //Just to be sure that the gas cost is lower than the estimate correction + const FAKE_GAS_COST = INTERNAL_TRANSACTION_ESTIMATED_CORRECTION - 1; + const EXPECTED_ESTIMATION = FAKE_GAS_COST; + + ierc20TransferStub.resolves(BigNumber.from(FAKE_GAS_COST)); + + const request: PaymentGasEstimationParams = { + relayRequest: { + ...FAKE_RELAY_REQUEST, + relayData: { + ...FAKE_RELAY_REQUEST.relayData, + gasPrice: FAKE_GAS_COST, + }, + }, + }; + + const estimation = await estimatePaymentGas(request); + + expect(estimation.toString()).to.equal(EXPECTED_ESTIMATION.toString()); + }); + }); + describe('estimateTokenTransferGas', function () { let ierc20TransferStub: SinonStub; @@ -383,7 +603,7 @@ describe('utils', function () { owner, smartWalletIndex: index, to: logic, - data: '0x0', + data: '0x00', isCustom: true, });