diff --git a/packages/starknet-snap/src/__tests__/helper.ts b/packages/starknet-snap/src/__tests__/helper.ts index b363226c..9ec61e2d 100644 --- a/packages/starknet-snap/src/__tests__/helper.ts +++ b/packages/starknet-snap/src/__tests__/helper.ts @@ -468,18 +468,16 @@ export function generateTransactionRequests({ }, ], includeDeploy: false, - resourceBounds: [ - { - l1_gas: { - max_amount: '0', - max_price_per_unit: '0', - }, - l2_gas: { - max_amount: '0', - max_price_per_unit: '0', - }, + resourceBounds: { + l1_gas: { + max_amount: '0', + max_price_per_unit: '0', }, - ], + l2_gas: { + max_amount: '0', + max_price_per_unit: '0', + }, + }, }); } diff --git a/packages/starknet-snap/src/rpcs/__tests__/helper.ts b/packages/starknet-snap/src/rpcs/__tests__/helper.ts index 0aee47be..ea50cd8c 100644 --- a/packages/starknet-snap/src/rpcs/__tests__/helper.ts +++ b/packages/starknet-snap/src/rpcs/__tests__/helper.ts @@ -3,9 +3,11 @@ import type { constants } from 'starknet'; import type { StarknetAccount } from '../../__tests__/helper'; import { generateAccounts, generateRandomValue } from '../../__tests__/helper'; +import { FeeTokenUnit } from '../../types/snapApi'; import type { SnapState } from '../../types/snapState'; import * as snapUiUtils from '../../ui/utils'; import { getExplorerUrl, shortenAddress, toJson } from '../../utils'; +import { mockEstimateFeeBulkResponse } from '../../utils/__tests__/helper'; import * as snapHelper from '../../utils/snap'; import * as snapUtils from '../../utils/snapUtils'; import * as starknetUtils from '../../utils/starknetUtils'; @@ -146,13 +148,14 @@ export function prepareRenderDisplayPrivateKeyAlertUI() { /** * + * @param result */ -export function prepareConfirmDialogInteractiveUI() { +export function prepareConfirmDialogInteractiveUI(result = true) { const confirmDialogSpy = jest.spyOn( snapHelper, 'createInteractiveConfirmDialog', ); - confirmDialogSpy.mockResolvedValue(true); + confirmDialogSpy.mockResolvedValue(result); return { confirmDialogSpy, }; @@ -230,3 +233,39 @@ export function generateRandomFee( ? randomFee.toString(10) : BigNumber.from(randomFee).toString(); } + +/** + * + * @param options0 + * @param options0.includeDeploy + * @param options0.unit + */ +export function mockGetEstimatedFeesResponse({ + includeDeploy = false, + unit = FeeTokenUnit.ETH, +}: { + includeDeploy?: boolean; + unit?: FeeTokenUnit; +}) { + const { + consolidatedFees: { suggestedMaxFee, overallFee, resourceBounds }, + estimateFeesResponse, + } = mockEstimateFeeBulkResponse(); + + const getEstimatedFeesResponse = { + suggestedMaxFee, + overallFee, + unit, + includeDeploy, + estimateResults: estimateFeesResponse, + resourceBounds, + }; + + const getEstimatedFeesSpy = jest.spyOn(starknetUtils, 'getEstimatedFees'); + getEstimatedFeesSpy.mockResolvedValue(getEstimatedFeesResponse); + + return { + getEstimatedFeesSpy, + getEstimatedFeesResponse, + }; +} diff --git a/packages/starknet-snap/src/rpcs/estimate-fee.test.ts b/packages/starknet-snap/src/rpcs/estimate-fee.test.ts index 9ab8a2af..2b23ed8b 100644 --- a/packages/starknet-snap/src/rpcs/estimate-fee.test.ts +++ b/packages/starknet-snap/src/rpcs/estimate-fee.test.ts @@ -2,13 +2,15 @@ import type { Invocations } from 'starknet'; import { constants, TransactionType } from 'starknet'; import type { Infer } from 'superstruct'; -import { generateEstimateFeesResponse } from '../__tests__/helper'; -import { FeeTokenUnit } from '../types/snapApi'; +import callsExamples from '../__tests__/fixture/callsExamples.json'; import { STARKNET_SEPOLIA_TESTNET_NETWORK } from '../utils/constants'; import { InvalidRequestParamsError } from '../utils/exceptions'; -import * as starknetUtils from '../utils/starknetUtils'; import type { TxVersionStruct } from '../utils/superstruct'; -import { mockAccount, prepareMockAccount } from './__tests__/helper'; +import { + mockAccount, + mockGetEstimatedFeesResponse, + prepareMockAccount, +} from './__tests__/helper'; import { estimateFee } from './estimate-fee'; import type { EstimateFeeParams } from './estimate-fee'; @@ -29,12 +31,7 @@ const prepareMockEstimateFee = ({ const invocations: Invocations = [ { type: TransactionType.INVOKE, - payload: { - contractAddress: - '0x00b28a089e7fb83debee4607b6334d687918644796b47d9e9e38ea8213833137', - entrypoint: 'functionName', - calldata: ['1', '1'], - }, + payload: callsExamples.singleCall.calls, }, ]; @@ -45,20 +42,13 @@ const prepareMockEstimateFee = ({ details: { version }, } as unknown as EstimateFeeParams; - const estimateResults = generateEstimateFeesResponse(); - - const estimateBulkFeeRespMock = { - suggestedMaxFee: BigInt(1000000000000000).toString(10), - overallFee: BigInt(1500000000000000).toString(10), - unit: FeeTokenUnit.ETH, - includeDeploy, - estimateResults, + return { + invocations, + request, + ...mockGetEstimatedFeesResponse({ + includeDeploy, + }), }; - - const getEstimatedFeesSpy = jest.spyOn(starknetUtils, 'getEstimatedFees'); - getEstimatedFeesSpy.mockResolvedValue(estimateBulkFeeRespMock); - - return { estimateBulkFeeRespMock, invocations, request, getEstimatedFeesSpy }; }; describe('estimateFee', () => { @@ -73,13 +63,21 @@ describe('estimateFee', () => { const chainId = constants.StarknetChainId.SN_SEPOLIA; const account = await mockAccount(chainId); prepareMockAccount(account, state); - const { request, getEstimatedFeesSpy, estimateBulkFeeRespMock } = - prepareMockEstimateFee({ - includeDeploy: false, - chainId, - address: account.address, - version: constants.TRANSACTION_VERSION.V1, - }); + const { + request, + getEstimatedFeesSpy, + getEstimatedFeesResponse: { + includeDeploy, + overallFee, + suggestedMaxFee, + unit, + }, + } = prepareMockEstimateFee({ + includeDeploy: false, + chainId, + address: account.address, + version: constants.TRANSACTION_VERSION.V1, + }); const result = await estimateFee.execute(request); @@ -94,10 +92,10 @@ describe('estimateFee', () => { }, ); expect(result).toStrictEqual({ - includeDeploy: estimateBulkFeeRespMock.includeDeploy, - overallFee: estimateBulkFeeRespMock.overallFee, - suggestedMaxFee: estimateBulkFeeRespMock.suggestedMaxFee, - unit: estimateBulkFeeRespMock.unit, + includeDeploy, + overallFee, + suggestedMaxFee, + unit, }); }); diff --git a/packages/starknet-snap/src/rpcs/execute-txn.test.ts b/packages/starknet-snap/src/rpcs/execute-txn.test.ts index 81f6d64a..56da650c 100644 --- a/packages/starknet-snap/src/rpcs/execute-txn.test.ts +++ b/packages/starknet-snap/src/rpcs/execute-txn.test.ts @@ -61,6 +61,7 @@ const prepareMockExecuteTxn = async ( includeDeploy: !accountDeployed, unit: 'wei' as FeeTokenUnit, estimateResults, + resourceBounds: estimateResults[0].resourceBounds, }; const getEstimatedFeesSpy = jest.spyOn(starknetUtils, 'getEstimatedFees'); diff --git a/packages/starknet-snap/src/rpcs/execute-txn.ts b/packages/starknet-snap/src/rpcs/execute-txn.ts index 75a6eeab..2e82eedf 100644 --- a/packages/starknet-snap/src/rpcs/execute-txn.ts +++ b/packages/starknet-snap/src/rpcs/execute-txn.ts @@ -116,7 +116,7 @@ export class ExecuteTxnRpc extends AccountRpcController< const { privateKey, publicKey } = this.account; const callsArray = Array.isArray(calls) ? calls : [calls]; - const { includeDeploy, suggestedMaxFee, estimateResults } = + const { includeDeploy, suggestedMaxFee, resourceBounds } = await getEstimatedFees( this.network, address, @@ -156,7 +156,7 @@ export class ExecuteTxnRpc extends AccountRpcController< addressIndex: this.account.addressIndex, maxFee: suggestedMaxFee, calls: formattedCalls, - resourceBounds: estimateResults.map((result) => result.resourceBounds), + resourceBounds, selectedFeeToken: version === constants.TRANSACTION_VERSION.V3 ? FeeToken.STRK @@ -210,10 +210,7 @@ export class ExecuteTxnRpc extends AccountRpcController< // TODO: we may also need to increment the nonce base on the input, if the account is not deployed nonce: accountDeployed ? details?.nonce : 1, maxFee: updatedRequest.maxFee, - resourceBounds: - updatedRequest.resourceBounds[ - updatedRequest.resourceBounds.length - 1 - ], + resourceBounds: updatedRequest.resourceBounds, version: updatedRequest.selectedFeeToken === FeeToken.STRK ? constants.TRANSACTION_VERSION.V3 diff --git a/packages/starknet-snap/src/state/request-state-manager.ts b/packages/starknet-snap/src/state/request-state-manager.ts index e5c851cc..f4a26f36 100644 --- a/packages/starknet-snap/src/state/request-state-manager.ts +++ b/packages/starknet-snap/src/state/request-state-manager.ts @@ -32,7 +32,9 @@ export class TransactionRequestStateManager extends StateManager['resourceBounds']; +export type ResourceBounds = Pick< + EstimateFee, + 'resourceBounds' +>['resourceBounds']; + +export type ResourceBoundsInBigInt = { + l1_gas: { max_amount: bigint; max_price_per_unit: bigint }; + l2_gas: { max_amount: bigint; max_price_per_unit: bigint }; +}; export type TransactionRequest = { id: string; @@ -44,7 +52,7 @@ export type TransactionRequest = { networkName: string; maxFee: string; calls: FormattedCallData[]; - resourceBounds: ResourceBounds[]; + resourceBounds: ResourceBounds; selectedFeeToken: string; includeDeploy: boolean; }; diff --git a/packages/starknet-snap/src/ui/controllers/user-input-event-controller.test.ts b/packages/starknet-snap/src/ui/controllers/user-input-event-controller.test.ts index 05b54532..6ce668c2 100644 --- a/packages/starknet-snap/src/ui/controllers/user-input-event-controller.test.ts +++ b/packages/starknet-snap/src/ui/controllers/user-input-event-controller.test.ts @@ -20,6 +20,7 @@ import { STARKNET_TESTNET_NETWORK, STRK_SEPOLIA_TESTNET, } from '../../utils/constants'; +import { ConsolidateFees } from '../../utils/fee'; import * as keyPairUtils from '../../utils/keyPair'; import * as StarknetUtils from '../../utils/starknetUtils'; import * as UiUtils from '../utils'; @@ -91,16 +92,16 @@ describe('UserInputEventController', () => { const mockEstimateFee = (feeToken: FeeToken) => { const getEstimatedFeesSpy = jest.spyOn(StarknetUtils, 'getEstimatedFees'); const mockEstimateFeeResponse = generateEstimateFeesResponse(); - const concatedFee = StarknetUtils.addFeesFromAllTransactions( - mockEstimateFeeResponse, - ); + const consolidateFeesObj = new ConsolidateFees(mockEstimateFeeResponse); + const consolidatedFees = consolidateFeesObj.serializate(); const mockGetEstimatedFeesResponse = { - suggestedMaxFee: concatedFee.suggestedMaxFee.toString(10), - overallFee: concatedFee.overall_fee.toString(10), + suggestedMaxFee: consolidatedFees.suggestedMaxFee, + overallFee: consolidatedFees.overallFee, unit: FeeTokenUnit[feeToken], includeDeploy: true, estimateResults: mockEstimateFeeResponse, + resourceBounds: consolidatedFees.resourceBounds, }; getEstimatedFeesSpy.mockResolvedValue(mockGetEstimatedFeesResponse); @@ -350,7 +351,7 @@ describe('UserInputEventController', () => { maxFee: transactionRequest.maxFee, selectedFeeToken: transactionRequest.selectedFeeToken, includeDeploy: transactionRequest.includeDeploy, - resourceBounds: [...transactionRequest.resourceBounds], + resourceBounds: transactionRequest.resourceBounds, }; const event = generateInputEvent({ diff --git a/packages/starknet-snap/src/ui/controllers/user-input-event-controller.ts b/packages/starknet-snap/src/ui/controllers/user-input-event-controller.ts index f9224a61..828ab73c 100644 --- a/packages/starknet-snap/src/ui/controllers/user-input-event-controller.ts +++ b/packages/starknet-snap/src/ui/controllers/user-input-event-controller.ts @@ -138,7 +138,9 @@ export class UserInputEventController { maxFee: request.maxFee, selectedFeeToken: request.selectedFeeToken, includeDeploy: request.includeDeploy, - resourceBounds: [...request.resourceBounds], + resourceBounds: { + ...request.resourceBounds, + }, }; } @@ -156,7 +158,7 @@ export class UserInputEventController { const requestTxnVersion = this.feeTokenToTransactionVersion(feeToken); - const { includeDeploy, suggestedMaxFee, estimateResults } = + const { includeDeploy, suggestedMaxFee, resourceBounds } = await getEstimatedFees( network, signer, @@ -196,9 +198,7 @@ export class UserInputEventController { request.maxFee = suggestedMaxFee; request.selectedFeeToken = feeToken; request.includeDeploy = includeDeploy; - request.resourceBounds = estimateResults.map( - (result) => result.resourceBounds, - ); + request.resourceBounds = resourceBounds; await updateExecuteTxnFlow(this.eventId, request); await this.reqStateMgr.upsertTransactionRequest(request); diff --git a/packages/starknet-snap/src/utils/__tests__/helper.ts b/packages/starknet-snap/src/utils/__tests__/helper.ts new file mode 100644 index 00000000..4327bece --- /dev/null +++ b/packages/starknet-snap/src/utils/__tests__/helper.ts @@ -0,0 +1,22 @@ +import { generateEstimateFeesResponse } from '../../__tests__/helper'; +import { ConsolidateFees } from '../fee'; +import * as starknetUtils from '../starknetUtils'; + +/** + * + */ +export function mockEstimateFeeBulkResponse() { + const estimateFeesResponse = generateEstimateFeesResponse(); + + const consolidatedFeesObj = new ConsolidateFees(estimateFeesResponse); + const consolidatedFees = consolidatedFeesObj.serializate(); + + const estimateBulkFeeSpy = jest.spyOn(starknetUtils, 'estimateFeeBulk'); + estimateBulkFeeSpy.mockResolvedValue(estimateFeesResponse); + + return { + estimateBulkFeeSpy, + estimateFeesResponse, + consolidatedFees, + }; +} diff --git a/packages/starknet-snap/src/utils/fee.test.ts b/packages/starknet-snap/src/utils/fee.test.ts new file mode 100644 index 00000000..94dd7bf3 --- /dev/null +++ b/packages/starknet-snap/src/utils/fee.test.ts @@ -0,0 +1,90 @@ +import { num as numUtils } from 'starknet'; + +import { generateEstimateFeesResponse } from '../__tests__/helper'; +import { ConsolidateFees } from './fee'; + +/* eslint-disable @typescript-eslint/naming-convention */ +describe('ConsolidateFees', () => { + const consolidateFees = (fees) => { + return fees.reduce( + (acc, fee) => { + acc.overallFee += fee.overall_fee; + acc.suggestedMaxFee += fee.suggestedMaxFee; + + acc.resourceBounds.l1_gas.max_amount += BigInt( + fee.resourceBounds.l1_gas.max_amount, + ); + acc.resourceBounds.l1_gas.max_price_per_unit += BigInt( + fee.resourceBounds.l1_gas.max_price_per_unit, + ); + acc.resourceBounds.l2_gas.max_amount += BigInt( + fee.resourceBounds.l2_gas.max_amount, + ); + acc.resourceBounds.l2_gas.max_price_per_unit += BigInt( + fee.resourceBounds.l2_gas.max_price_per_unit, + ); + + return acc; + }, + { + overallFee: BigInt(0), + suggestedMaxFee: BigInt(0), + resourceBounds: { + l1_gas: { + max_amount: BigInt(0), + max_price_per_unit: BigInt(0), + }, + l2_gas: { + max_amount: BigInt(0), + max_price_per_unit: BigInt(0), + }, + }, + }, + ); + }; + + it('consolidates fees', () => { + const fees = generateEstimateFeesResponse(); + const consolidatedFeesObj = new ConsolidateFees(fees); + + const { overallFee, suggestedMaxFee, resourceBounds } = + consolidateFees(fees); + + expect(consolidatedFeesObj.overallFee).toStrictEqual(overallFee); + expect(consolidatedFeesObj.suggestedMaxFee).toStrictEqual(suggestedMaxFee); + expect(consolidatedFeesObj.resourceBounds).toStrictEqual(resourceBounds); + }); + + describe('serializate', () => { + it('serializes fees', () => { + const fees = generateEstimateFeesResponse(); + const consolidatedFeesObj = new ConsolidateFees(fees); + + const serializedFee = consolidatedFeesObj.serializate(); + + expect(serializedFee).toStrictEqual({ + overallFee: consolidatedFeesObj.overallFee.toString(10), + suggestedMaxFee: consolidatedFeesObj.suggestedMaxFee.toString(10), + resourceBounds: { + l1_gas: { + max_amount: numUtils.toHexString( + consolidatedFeesObj.resourceBounds.l1_gas.max_amount, + ), + max_price_per_unit: numUtils.toHexString( + consolidatedFeesObj.resourceBounds.l1_gas.max_price_per_unit, + ), + }, + l2_gas: { + max_amount: numUtils.toHexString( + consolidatedFeesObj.resourceBounds.l2_gas.max_amount, + ), + max_price_per_unit: numUtils.toHexString( + consolidatedFeesObj.resourceBounds.l2_gas.max_price_per_unit, + ), + }, + }, + }); + }); + }); +}); +/* eslint-enable */ diff --git a/packages/starknet-snap/src/utils/fee.ts b/packages/starknet-snap/src/utils/fee.ts new file mode 100644 index 00000000..fa023495 --- /dev/null +++ b/packages/starknet-snap/src/utils/fee.ts @@ -0,0 +1,120 @@ +import type { EstimateFee } from 'starknet'; +import { num as numUtils } from 'starknet'; + +import type { + ResourceBounds, + ResourceBoundsInBigInt, +} from '../types/snapState'; + +export type ConsolidatedFees = { + overallFee: bigint; + suggestedMaxFee: bigint; + resourceBounds: ResourceBoundsInBigInt; +}; + +export type SerializatedConsolidatedFees = { + overallFee: string; + suggestedMaxFee: string; + resourceBounds: ResourceBounds; +}; + +/* eslint-disable @typescript-eslint/naming-convention */ +export class ConsolidateFees { + fees: EstimateFee[]; + + overallFee: bigint; + + suggestedMaxFee: bigint; + + resourceBounds: ResourceBoundsInBigInt; + + constructor(fees: EstimateFee[]) { + this.fees = fees; + const { overallFee, suggestedMaxFee, resourceBounds } = + this.consolidateFee(); + this.overallFee = overallFee; + this.suggestedMaxFee = suggestedMaxFee; + this.resourceBounds = resourceBounds; + } + + /** + * Consolidate the fees. + * + * @returns The consolidated fees. + */ + protected consolidateFee(): ConsolidatedFees { + const consolidateResult = this.fees.reduce( + (acc, fee) => { + acc.overallFee += fee.overall_fee; + acc.suggestedMaxFee += fee.suggestedMaxFee; + + acc.resourceBounds.l1_gas.max_amount += BigInt( + fee.resourceBounds.l1_gas.max_amount, + ); + acc.resourceBounds.l1_gas.max_price_per_unit += BigInt( + fee.resourceBounds.l1_gas.max_price_per_unit, + ); + acc.resourceBounds.l2_gas.max_amount += BigInt( + fee.resourceBounds.l2_gas.max_amount, + ); + acc.resourceBounds.l2_gas.max_price_per_unit += BigInt( + fee.resourceBounds.l2_gas.max_price_per_unit, + ); + + return acc; + }, + { + overallFee: BigInt(0), + suggestedMaxFee: BigInt(0), + resourceBounds: { + l1_gas: { + max_amount: BigInt(0), + max_price_per_unit: BigInt(0), + }, + l2_gas: { + max_amount: BigInt(0), + max_price_per_unit: BigInt(0), + }, + }, + }, + ); + + return { + overallFee: consolidateResult.overallFee, + suggestedMaxFee: consolidateResult.suggestedMaxFee, + resourceBounds: consolidateResult.resourceBounds, + }; + } + + /** + * Serialize the consolidated fees result into a Object that contains suggestedMaxFee, overallFee and resourceBounds in string. + * + * @returns A serializated object. + */ + serializate(): SerializatedConsolidatedFees { + return { + suggestedMaxFee: this.suggestedMaxFee.toString(10), + overallFee: this.overallFee.toString(10), + resourceBounds: { + // convert to hex string for serialization in starknet.js when using STRK token to pay the fee. + l1_gas: { + max_amount: numUtils.toHexString( + this.resourceBounds.l1_gas.max_amount, + ), + max_price_per_unit: numUtils.toHexString( + this.resourceBounds.l1_gas.max_price_per_unit, + ), + }, + l2_gas: { + max_amount: numUtils.toHexString( + this.resourceBounds.l2_gas.max_amount, + ), + max_price_per_unit: numUtils.toHexString( + this.resourceBounds.l2_gas.max_price_per_unit, + ), + }, + }, + }; + } +} +/* eslint-enable */ diff --git a/packages/starknet-snap/src/utils/starknetUtils.test.ts b/packages/starknet-snap/src/utils/starknetUtils.test.ts index 2bfc56e7..5b74df56 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.test.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.test.ts @@ -1,11 +1,12 @@ -import type { EstimateFee, Invocations } from 'starknet'; +import type { Invocations } from 'starknet'; import { constants, TransactionType } from 'starknet'; -import { generateEstimateFeesResponse } from '../__tests__/helper'; +import callsExamples from '../__tests__/fixture/callsExamples.json'; import { mockAccount, prepareMockAccount } from '../rpcs/__tests__/helper'; import { FeeTokenUnit } from '../types/snapApi'; import type { SnapState } from '../types/snapState'; import type { TransactionVersion } from '../types/starknet'; +import { mockEstimateFeeBulkResponse } from './__tests__/helper'; import { STARKNET_SEPOLIA_TESTNET_NETWORK } from './constants'; import * as starknetUtils from './starknetUtils'; @@ -17,22 +18,14 @@ describe('getEstimatedFees', () => { transactions: [], }; - const suggestedMaxFee = BigInt('0xc'); - const overallFee = BigInt('0xa'); - - const prepareSpy = async (deployed: boolean) => { + const prepareGetEstimatedFees = async (deployed: boolean) => { const chainId = constants.StarknetChainId.SN_SEPOLIA; const account = await mockAccount(chainId); const invocations: Invocations = [ { type: TransactionType.INVOKE, - payload: { - contractAddress: - '0x00b28a089e7fb83debee4607b6334d687918644796b47d9e9e38ea8213833137', - entrypoint: 'functionName', - calldata: ['1', '1'], - }, + payload: callsExamples.singleCall.calls, }, ]; @@ -40,28 +33,11 @@ describe('getEstimatedFees', () => { const accountDeployedSpy = jest.spyOn(starknetUtils, 'isAccountDeployed'); accountDeployedSpy.mockResolvedValue(deployed); - const estimateResults = generateEstimateFeesResponse(); - const { resourceBounds } = estimateResults[0]; - - const estimateFeeResp = { - // eslint-disable-next-line @typescript-eslint/naming-convention - overall_fee: overallFee, - // eslint-disable-next-line @typescript-eslint/naming-convention - gas_consumed: BigInt('0x0'), - suggestedMaxFee, - // eslint-disable-next-line @typescript-eslint/naming-convention - gas_price: BigInt('0x0'), - resourceBounds, - } as unknown as EstimateFee; - const estimateBulkFeeSpy = jest.spyOn(starknetUtils, 'estimateFeeBulk'); - estimateBulkFeeSpy.mockResolvedValue([estimateFeeResp]); - return { account, invocations, accountDeployedSpy, - estimateBulkFeeSpy, - estimateFeeResp, + ...mockEstimateFeeBulkResponse(), }; }; @@ -88,8 +64,13 @@ describe('getEstimatedFees', () => { expectedUnit: FeeTokenUnit; }) => { const network = STARKNET_SEPOLIA_TESTNET_NETWORK; - const { account, invocations, estimateBulkFeeSpy, estimateFeeResp } = - await prepareSpy(true); + const { + account, + invocations, + estimateBulkFeeSpy, + estimateFeesResponse, + consolidatedFees: { suggestedMaxFee, overallFee, resourceBounds }, + } = await prepareGetEstimatedFees(true); const call = invocations[0]; const resp = await starknetUtils.getEstimatedFees( @@ -118,19 +99,25 @@ describe('getEstimatedFees', () => { }, ); expect(resp).toStrictEqual({ - suggestedMaxFee: suggestedMaxFee.toString(10), - overallFee: overallFee.toString(10), + suggestedMaxFee, + overallFee, unit: expectedUnit, // to verify if the unit is return correctly includeDeploy: false, - estimateResults: [estimateFeeResp], + estimateResults: estimateFeesResponse, + resourceBounds, }); }, ); it('estimates fees with account deploy payload if the account is not deployed', async () => { const network = STARKNET_SEPOLIA_TESTNET_NETWORK; - const { account, estimateBulkFeeSpy, estimateFeeResp, invocations } = - await prepareSpy(false); + const { + account, + estimateBulkFeeSpy, + consolidatedFees: { suggestedMaxFee, overallFee, resourceBounds }, + estimateFeesResponse, + invocations, + } = await prepareGetEstimatedFees(false); const deployAccountpayload = starknetUtils.createAccountDeployPayload( account.address, account.publicKey, @@ -162,11 +149,12 @@ describe('getEstimatedFees', () => { undefined, ); expect(resp).toStrictEqual({ - suggestedMaxFee: suggestedMaxFee.toString(10), - overallFee: overallFee.toString(10), + suggestedMaxFee, + overallFee, unit: FeeTokenUnit.ETH, includeDeploy: true, - estimateResults: [estimateFeeResp], + resourceBounds, + estimateResults: estimateFeesResponse, }); }); }); diff --git a/packages/starknet-snap/src/utils/starknetUtils.ts b/packages/starknet-snap/src/utils/starknetUtils.ts index 39823a96..a8c037bc 100644 --- a/packages/starknet-snap/src/utils/starknetUtils.ts +++ b/packages/starknet-snap/src/utils/starknetUtils.ts @@ -45,7 +45,12 @@ import { FeeTokenUnit, type RpcV4GetTransactionReceiptResponse, } from '../types/snapApi'; -import type { Network, SnapState, Transaction } from '../types/snapState'; +import type { + Network, + ResourceBounds, + SnapState, + Transaction, +} from '../types/snapState'; import { TransactionType } from '../types/snapState'; import type { DeployAccountPayload, @@ -70,6 +75,7 @@ import { BlockIdentifierEnum, } from './constants'; import { DeployRequiredError, UpgradeRequiredError } from './exceptions'; +import { ConsolidateFees } from './fee'; import { hexToString } from './formatter-utils'; import { getAddressKey } from './keyPair'; import { logger } from './logger'; @@ -284,7 +290,7 @@ export const deployAccount = async ( network: Network, contractAddress: string, contractCallData: RawCalldata, - addressSalt: numUtils.BigNumberish, + addressSalt: string, privateKey: string | Uint8Array, cairoVersion?: CairoVersion, invocationsDetails?: UniversalDetails, @@ -992,24 +998,6 @@ export const isAccountDeployed = async (network: Network, address: string) => { } }; -export const addFeesFromAllTransactions = ( - fees: EstimateFee[], -): Pick => { - let overallFee = numUtils.toBigInt(0); - let suggestedMaxFee = numUtils.toBigInt(0); - - fees.forEach((fee) => { - overallFee += fee.overall_fee; - suggestedMaxFee += fee.suggestedMaxFee; - }); - - return { - // eslint-disable-next-line @typescript-eslint/naming-convention - overall_fee: overallFee, - suggestedMaxFee, - }; -}; - export const _validateAndParseAddressFn = _validateAndParseAddress; export const validateAndParseAddress = ( address: numUtils.BigNumberish, @@ -1077,6 +1065,7 @@ export async function getEstimatedFees( overallFee: string; unit: FeeTokenUnit; includeDeploy: boolean; + resourceBounds: ResourceBounds; estimateResults: EstimateFee[]; }> { const accountDeployed = await isAccountDeployed(network, address); @@ -1089,7 +1078,7 @@ export async function getEstimatedFees( }); } - const estimateBulkFeeResp = await estimateFeeBulk( + const estimateResults = await estimateFeeBulk( network, address, privateKey, @@ -1097,17 +1086,19 @@ export async function getEstimatedFees( invocationsDetails, ); - const estimateFeeResp = addFeesFromAllTransactions(estimateBulkFeeResp); + const consolidateFeesObj = new ConsolidateFees(estimateResults); + const consolidateResult = consolidateFeesObj.serializate(); return { - suggestedMaxFee: estimateFeeResp.suggestedMaxFee.toString(10), - overallFee: estimateFeeResp.overall_fee.toString(10), + suggestedMaxFee: consolidateResult.suggestedMaxFee, + overallFee: consolidateResult.overallFee, + resourceBounds: consolidateResult.resourceBounds, unit: invocationsDetails?.version === constants.TRANSACTION_VERSION.V3 ? FeeTokenUnit.STRK : FeeTokenUnit.ETH, includeDeploy: !accountDeployed, - estimateResults: estimateBulkFeeResp, + estimateResults, }; }