From 5652eb211e1accaefbb2f24bcb756ccf9170c07a Mon Sep 17 00:00:00 2001 From: Toni Tabak Date: Wed, 6 Dec 2023 02:20:58 +0100 Subject: [PATCH] feat: account preferred transaction version, setup v3 --- __tests__/config/fixtures.ts | 5 +- src/account/default.ts | 233 ++++++++++++++++++++++------------- src/channel/rpc_0_6.ts | 18 +-- src/types/account.ts | 12 +- src/utils/stark.ts | 35 +++++- 5 files changed, 193 insertions(+), 110 deletions(-) diff --git a/__tests__/config/fixtures.ts b/__tests__/config/fixtures.ts index a8748fbd5..72d14dd5d 100644 --- a/__tests__/config/fixtures.ts +++ b/__tests__/config/fixtures.ts @@ -8,6 +8,7 @@ import { LegacyCompiledContract, waitForTransactionOptions, } from '../../src/types'; +import { ETransactionVersion } from '../../src/types/api'; import { toHex } from '../../src/utils/num'; const readContract = (name: string): LegacyCompiledContract => @@ -71,7 +72,9 @@ export const getTestAccount = (provider: ProviderInterface) => { return new Account( provider, toHex(process.env.TEST_ACCOUNT_ADDRESS || ''), - process.env.TEST_ACCOUNT_PRIVATE_KEY || '' + process.env.TEST_ACCOUNT_PRIVATE_KEY || '', + undefined, + process.env.TX_VERSION === 'v3' ? ETransactionVersion.V3 : undefined // TODO: enable setup to test diff TX versions ); }; diff --git a/src/account/default.ts b/src/account/default.ts index a4fa99ca5..5482d3102 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -25,9 +25,9 @@ import { EstimateFeeAction, EstimateFeeBulk, EstimateFeeDetails, + EstimateFeeResponse, Invocation, Invocations, - InvocationsDetails, InvocationsSignerDetails, InvokeFunctionResponse, MultiDeployContractResponse, @@ -40,12 +40,7 @@ import { TypedData, UniversalDeployerContractPayload, } from '../types'; -import { - EDataAvailabilityMode, - ETransactionVersion, - ETransactionVersion2, - ETransactionVersion3, -} from '../types/api'; +import { ETransactionVersion, ETransactionVersion3 } from '../types/api'; import { CallData } from '../utils/calldata'; import { extractContractHashes, isSierra } from '../utils/contract'; import { starkCurve } from '../utils/ec'; @@ -58,6 +53,8 @@ import { estimatedFeeToMaxFee, formatSignature, randomAddress, + toTransactionVersion, + v3Details, } from '../utils/stark'; import { getExecuteCalldata } from '../utils/transaction'; import { getMessageHash } from '../utils/typedData'; @@ -70,11 +67,14 @@ export class Account extends Provider implements AccountInterface { public cairoVersion: CairoVersion; + readonly transactionVersion: ETransactionVersion.V2 | ETransactionVersion.V3; + constructor( providerOrOptions: ProviderOptions | ProviderInterface, address: string, pkOrSigner: Uint8Array | string | SignerInterface, - cairoVersion?: CairoVersion + cairoVersion?: CairoVersion, + transactionVersion: ETransactionVersion.V2 | ETransactionVersion.V3 = ETransactionVersion.V2 // TODO: Discuss this, set to v2 for backward compatibility ) { super(providerOrOptions); this.address = address.toLowerCase(); @@ -86,6 +86,15 @@ export class Account extends Provider implements AccountInterface { if (cairoVersion) { this.cairoVersion = cairoVersion.toString() as CairoVersion; } + this.transactionVersion = transactionVersion; + } + + // provided version or contract based preferred transactionVersion + private getPreferredVersion(type2: ETransactionVersion, type3: ETransactionVersion) { + if (this.transactionVersion === ETransactionVersion.V3) return type3; + if (this.transactionVersion === ETransactionVersion.V2) return type2; + + return ETransactionVersion.V3; } public async getNonce(blockIdentifier?: BlockIdentifier): Promise { @@ -117,18 +126,23 @@ export class Account extends Provider implements AccountInterface { public async estimateFee( calls: AllowArray, - estimateFeeDetails?: EstimateFeeDetails | undefined + estimateFeeDetails: EstimateFeeDetails = {} ): Promise { return this.estimateInvokeFee(calls, estimateFeeDetails); } public async estimateInvokeFee( calls: AllowArray, - { nonce: providedNonce, blockIdentifier }: EstimateFeeDetails = {} + details: EstimateFeeDetails = {} ): Promise { + const { nonce: providedNonce, blockIdentifier, version: providedVersion } = details; + const transactions = Array.isArray(calls) ? calls : [calls]; const nonce = toBigInt(providedNonce ?? (await this.getNonce())); - const version = ETransactionVersion.F1; + const version = toTransactionVersion( + this.getPreferredVersion(ETransactionVersion.F1, ETransactionVersion.F3), + providedVersion + ); const chainId = await this.getChainId(); const signerDetails: InvocationsSignerDetails = { @@ -138,29 +152,35 @@ export class Account extends Provider implements AccountInterface { version, chainId, cairoVersion: await this.getCairoVersion(), + ...v3Details(details), }; const invocation = await this.buildInvocation(transactions, signerDetails); - const response = await super.getInvokeEstimateFee( + const estimateFeeResponse = await super.getInvokeEstimateFee( { ...invocation }, - { version, nonce }, + { version, nonce, ...v3Details(details) }, blockIdentifier ); - const suggestedMaxFee = estimatedFeeToMaxFee(response.overall_fee); - return { - ...response, - suggestedMaxFee, + ...estimateFeeResponse, + suggestedMaxFee: estimatedFeeToMaxFee(estimateFeeResponse.overall_fee), + resourceBounds: estimateFeeToBounds(estimateFeeResponse), }; } public async estimateDeclareFee( { contract, classHash: providedClassHash, casm, compiledClassHash }: DeclareContractPayload, - { blockIdentifier, nonce: providedNonce }: EstimateFeeDetails = {} + details: EstimateFeeDetails = {} ): Promise { + const { blockIdentifier, nonce: providedNonce, version: providedVersion } = details; const nonce = toBigInt(providedNonce ?? (await this.getNonce())); - const version = !isSierra(contract) ? ETransactionVersion.F1 : ETransactionVersion.F2; + const version = toTransactionVersion( + !isSierra(contract) + ? ETransactionVersion.F1 + : this.getPreferredVersion(ETransactionVersion.F2, ETransactionVersion.F3), + providedVersion + ); const chainId = await this.getChainId(); const declareContractTransaction = await this.buildDeclarePayload( @@ -172,19 +192,20 @@ export class Account extends Provider implements AccountInterface { walletAddress: this.address, maxFee: ZERO, cairoVersion: undefined, // unused parameter + ...v3Details(details), } ); - const response = await super.getDeclareEstimateFee( + const estimateFeeResponse = await super.getDeclareEstimateFee( declareContractTransaction, - { version, nonce }, + { version, nonce, ...v3Details(details) }, blockIdentifier ); - const suggestedMaxFee = estimatedFeeToMaxFee(response.overall_fee); return { - ...response, - suggestedMaxFee, + ...estimateFeeResponse, + suggestedMaxFee: estimatedFeeToMaxFee(estimateFeeResponse.overall_fee), + resourceBounds: estimateFeeToBounds(estimateFeeResponse), }; } @@ -195,9 +216,13 @@ export class Account extends Provider implements AccountInterface { constructorCalldata = [], contractAddress: providedContractAddress, }: DeployAccountContractPayload, - { blockIdentifier }: EstimateFeeDetails = {} + details: EstimateFeeDetails = {} ): Promise { - const version = ETransactionVersion.F1; + const { blockIdentifier, version: providedVersion } = details; + const version = toTransactionVersion( + this.getPreferredVersion(ETransactionVersion.F1, ETransactionVersion.F3), + providedVersion + ); // TODO: Can Cairo0 be deployed with F3 ? const nonce = ZERO; // DEPLOY_ACCOUNT transaction will have a nonce zero as it is the first transaction in the account const chainId = await this.getChainId(); @@ -210,25 +235,26 @@ export class Account extends Provider implements AccountInterface { walletAddress: this.address, // unused parameter maxFee: ZERO, cairoVersion: undefined, // unused parameter, + ...v3Details(details), } ); - const response = await super.getDeployAccountEstimateFee( + const estimateFeeResponse = await super.getDeployAccountEstimateFee( { ...payload }, - { version, nonce }, + { version, nonce, ...v3Details(details) }, blockIdentifier ); - const suggestedMaxFee = estimatedFeeToMaxFee(response.overall_fee); return { - ...response, - suggestedMaxFee, + ...estimateFeeResponse, + suggestedMaxFee: estimatedFeeToMaxFee(estimateFeeResponse.overall_fee), + resourceBounds: estimateFeeToBounds(estimateFeeResponse), }; } public async estimateDeployFee( payload: UniversalDeployerContractPayload | UniversalDeployerContractPayload[], - transactionsDetail?: InvocationsDetails | undefined + transactionsDetail: EstimateFeeDetails = {} ): Promise { const calls = this.buildUDCContractPayload(payload); return this.estimateInvokeFee(calls, transactionsDetail); @@ -236,55 +262,68 @@ export class Account extends Provider implements AccountInterface { public async estimateFeeBulk( invocations: Invocations, - { nonce, blockIdentifier }: EstimateFeeDetails = {} + details: EstimateFeeDetails = {} ): Promise { + const { nonce, blockIdentifier } = details; const accountInvocations = await this.accountInvocationsFactory(invocations, { - versions: [ETransactionVersion.F1, ETransactionVersion.F2], + versions: [ + ETransactionVersion.F1, // non-sierra + this.getPreferredVersion(ETransactionVersion.F2, ETransactionVersion.F3), // sierra + ], nonce, blockIdentifier, + ...v3Details(details), }); - const response = await super.getEstimateFeeBulk(accountInvocations, { + const EstimateFeeResponseBulk = await super.getEstimateFeeBulk(accountInvocations, { blockIdentifier, }); - return [].concat(response as []).map((elem: any) => { - const suggestedMaxFee = estimatedFeeToMaxFee(elem.overall_fee); + return [].concat(EstimateFeeResponseBulk as []).map((elem: EstimateFeeResponse) => { return { ...elem, - suggestedMaxFee, + suggestedMaxFee: estimatedFeeToMaxFee(elem.overall_fee), + resourceBounds: estimateFeeToBounds(elem), }; }); } public async buildInvocation( call: Array, - signerDetails: InvocationsSignerDetails + details: InvocationsSignerDetails ): Promise { const calldata = getExecuteCalldata(call, await this.getCairoVersion()); - const signature = await this.signer.signTransaction(call, signerDetails); + const signature = await this.signer.signTransaction(call, details); return { contractAddress: this.address, calldata, signature, + ...v3Details(details), }; } public async execute( calls: AllowArray, abis: Abi[] | undefined = undefined, - transactionsDetail: InvocationsDetails = {} + details: EstimateFeeDetails = {} ): Promise { const transactions = Array.isArray(calls) ? calls : [calls]; - const nonce = toBigInt(transactionsDetail.nonce ?? (await this.getNonce())); + const nonce = toBigInt(details.nonce ?? (await this.getNonce())); + const version = toTransactionVersion( + this.getPreferredVersion(ETransactionVersion.V1, ETransactionVersion.V3), // TODO: does this depend on cairo version ? + details.version + ); const maxFee = - transactionsDetail.maxFee ?? + details.maxFee ?? (await this.getSuggestedMaxFee( { type: TransactionType.INVOKE, payload: calls }, - transactionsDetail + { + ...details, + version, + } )); - const version = ETransactionVersion.V1; + const chainId = await this.getChainId(); const signerDetails: InvocationsSignerDetails = { @@ -294,6 +333,7 @@ export class Account extends Provider implements AccountInterface { version, chainId, cairoVersion: await this.getCairoVersion(), + ...v3Details(details), }; const signature = await this.signer.signTransaction(transactions, signerDetails, abis); @@ -306,6 +346,7 @@ export class Account extends Provider implements AccountInterface { nonce, maxFee, version, + ...v3Details(details), } ); } @@ -318,7 +359,7 @@ export class Account extends Provider implements AccountInterface { */ public async declareIfNot( payload: DeclareContractPayload, - transactionsDetail: InvocationsDetails = {} + transactionsDetail: EstimateFeeDetails = {} ): Promise { const declareContractPayload = extractContractHashes(payload); try { @@ -334,25 +375,36 @@ export class Account extends Provider implements AccountInterface { public async declare( payload: DeclareContractPayload, - transactionsDetail: InvocationsDetails = {} + details: EstimateFeeDetails = {} ): Promise { const declareContractPayload = extractContractHashes(payload); + const { maxFee, nonce, version: providedVersion } = details; + const version = toTransactionVersion( + !isSierra(payload.contract) + ? ETransactionVersion.V1 + : this.getPreferredVersion(ETransactionVersion.V2, ETransactionVersion.V3), + providedVersion + ); const declareDetails: InvocationsSignerDetails = { - nonce: toBigInt(transactionsDetail.nonce ?? (await this.getNonce())), + nonce: toBigInt(nonce ?? (await this.getNonce())), maxFee: - transactionsDetail.maxFee ?? + maxFee ?? (await this.getSuggestedMaxFee( { type: TransactionType.DECLARE, payload: declareContractPayload, }, - transactionsDetail + { + ...details, + version, + } )), - version: !isSierra(payload.contract) ? ETransactionVersion.V1 : ETransactionVersion.V2, + version, chainId: await this.getChainId(), walletAddress: this.address, cairoVersion: undefined, + ...v3Details(details), }; const declareContractTransaction = await this.buildDeclarePayload( @@ -365,7 +417,7 @@ export class Account extends Provider implements AccountInterface { public async deploy( payload: UniversalDeployerContractPayload | UniversalDeployerContractPayload[], - details?: InvocationsDetails | undefined + details: EstimateFeeDetails = {} ): Promise { const params = [].concat(payload as []).map((it) => { const { @@ -411,7 +463,7 @@ export class Account extends Provider implements AccountInterface { public async deployContract( payload: UniversalDeployerContractPayload | UniversalDeployerContractPayload[], - details?: InvocationsDetails | undefined + details: EstimateFeeDetails = {} ): Promise { const deployTx = await this.deploy(payload, details); const txReceipt = await this.waitForTransaction(deployTx.transaction_hash); @@ -420,7 +472,7 @@ export class Account extends Provider implements AccountInterface { public async declareAndDeploy( payload: DeclareAndDeployContractPayload, - details?: InvocationsDetails | undefined + details: EstimateFeeDetails = {} ): Promise { const { constructorCalldata, salt, unique } = payload; let declare = await this.declareIfNot(payload, details); @@ -444,9 +496,12 @@ export class Account extends Provider implements AccountInterface { addressSalt = 0, contractAddress: providedContractAddress, }: DeployAccountContractPayload, - transactionsDetail: InvocationsDetails = {} + details: EstimateFeeDetails = {} ): Promise { - const version = ETransactionVersion.V1; + const version = toTransactionVersion( + this.getPreferredVersion(ETransactionVersion.V1, ETransactionVersion.V3), + details.version + ); const nonce = ZERO; // DEPLOY_ACCOUNT transaction will have a nonce zero as it is the first transaction in the account const chainId = await this.getChainId(); @@ -456,7 +511,7 @@ export class Account extends Provider implements AccountInterface { calculateContractAddressFromHash(addressSalt, classHash, compiledCalldata, 0); const maxFee = - transactionsDetail.maxFee ?? + details.maxFee ?? (await this.getSuggestedMaxFee( { type: TransactionType.DEPLOY_ACCOUNT, @@ -467,7 +522,7 @@ export class Account extends Provider implements AccountInterface { contractAddress, }, }, - transactionsDetail + details )); const signature = await this.signer.signDeployAccountTransaction({ @@ -479,6 +534,7 @@ export class Account extends Provider implements AccountInterface { maxFee, version, nonce, + ...v3Details(details), }); return this.deployAccountContract( @@ -487,6 +543,7 @@ export class Account extends Provider implements AccountInterface { nonce, maxFee, version, + ...v3Details(details), } ); } @@ -544,7 +601,11 @@ export class Account extends Provider implements AccountInterface { break; default: - feeEstimate = { suggestedMaxFee: ZERO, overall_fee: ZERO }; + feeEstimate = { + suggestedMaxFee: ZERO, + overall_fee: ZERO, + resourceBounds: estimateFeeToBounds(ZERO), + }; break; } @@ -573,6 +634,7 @@ export class Account extends Provider implements AccountInterface { classHash, compiledClassHash: compiledClassHash as string, // TODO: TS Nekuzi da v2 nemora imat a v3 mora i da je throvano ako nije definiran senderAddress: details.walletAddress, + ...v3Details(details), }); return { @@ -603,6 +665,7 @@ export class Account extends Provider implements AccountInterface { contractAddress, addressSalt, constructorCalldata: compiledCalldata, + ...v3Details(details), }); return { @@ -642,12 +705,20 @@ export class Account extends Provider implements AccountInterface { public async simulateTransaction( invocations: Invocations, - { nonce, blockIdentifier, skipValidate, skipExecute }: SimulateTransactionDetails = {} + details: SimulateTransactionDetails = {} ): Promise { + const { nonce, blockIdentifier, skipValidate, skipExecute, version } = details; const accountInvocations = await this.accountInvocationsFactory(invocations, { - versions: [ETransactionVersion.V1, ETransactionVersion.V2], + versions: [ + toTransactionVersion(ETransactionVersion.V1), // non-sierra + toTransactionVersion( + this.getPreferredVersion(ETransactionVersion.V2, ETransactionVersion.V3), + version + ), + ], nonce, blockIdentifier, + ...v3Details(details), }); return super.getSimulateTransaction(accountInvocations, { @@ -659,9 +730,10 @@ export class Account extends Provider implements AccountInterface { public async accountInvocationsFactory( invocations: Invocations, - { versions, nonce, blockIdentifier }: AccountInvocationsFactoryDetails + details: AccountInvocationsFactoryDetails ) { - const version = versions[0]; + const { versions, nonce, blockIdentifier } = details; + const version = versions[1]; // TODO: ovdje je bilo 0 prije a tribalo bi bit 1 LOL const safeNonce = await this.getNonceSafe(nonce); const chainId = await this.getChainId(); @@ -675,32 +747,15 @@ export class Account extends Provider implements AccountInterface { return Promise.all( ([] as Invocations).concat(invocations).map(async (transaction, index: number) => { const txPayload: any = 'payload' in transaction ? transaction.payload : transaction; - let signerDetails: InvocationsSignerDetails; - if (Object.values(ETransactionVersion2).includes(version as any)) { - signerDetails = { - walletAddress: this.address, - nonce: toBigInt(Number(safeNonce) + index), - maxFee: ZERO, - version: version as ETransactionVersion2, - chainId, - cairoVersion, - }; - } else if (Object.values(ETransactionVersion3).includes(version as any)) { - signerDetails = { - walletAddress: this.address, - nonce: toBigInt(Number(safeNonce) + index), - version: version as ETransactionVersion3, - chainId, - cairoVersion, - resourceBounds: estimateFeeToBounds(ZERO), - // TODO: this is defaults also inherit data from params - tip: 0, - paymasterData: [], - accountDeploymentData: [], - nonceDataAvailabilityMode: EDataAvailabilityMode.L1, - feeDataAvailabilityMode: EDataAvailabilityMode.L1, - }; - } else throw Error('un-supported version'); + const signerDetails: InvocationsSignerDetails = { + walletAddress: this.address, + nonce: toBigInt(Number(safeNonce) + index), + maxFee: ZERO, + version, + chainId, + cairoVersion, + ...v3Details(details), + }; const common = { type: transaction.type, version, diff --git a/src/channel/rpc_0_6.ts b/src/channel/rpc_0_6.ts index d2b87e82b..275b3453c 100644 --- a/src/channel/rpc_0_6.ts +++ b/src/channel/rpc_0_6.ts @@ -29,22 +29,6 @@ import { Block, getDefaultNodeUrl, isV3Tx, wait } from '../utils/provider'; import { decompressProgram, signatureToHexArray } from '../utils/stark'; import { getVersionsByType } from '../utils/transaction'; -/* function detailsToV3DefaultDetails(details: InvocationsDetailsWithNonce) { - if (!isV3Tx(details)) throw Error('detailsToV3Details: Transaction is not V3'); - - return { - ...details, - resource_bounds: details.resourceBounds, - tip: toHex(details.tip || 0), - paymaster_data: details.paymasterData ? details.paymasterData.map((it) => toHex(it)) : [], - account_deployment_data: details.accountDeploymentData - ? details.accountDeploymentData.map((it) => toHex(it)) - : [], - nonce_data_availability_mode: details.nonceDataAvailabilityMode || 'L1', - fee_data_availability_mode: details.feeDataAvailabilityMode || 'L1', - }; -} */ - const defaultOptions = { headers: { 'Content-Type': 'application/json' }, blockIdentifier: BlockTag.pending, @@ -622,7 +606,7 @@ export class RpcChannel { if (invocation.type === TransactionType.INVOKE) { return { // v0 v1 v3 - type: RPC.ETransactionType.INVOKE, // Diff between sequencer and rpc invoke type + type: RPC.ETransactionType.INVOKE, // TODO: Diff between sequencer and rpc invoke type sender_address: invocation.contractAddress, calldata: CallData.toHex(invocation.calldata), version: toHex(invocation.version || defaultVersions.v3), diff --git a/src/types/account.ts b/src/types/account.ts index 4cd8f8d55..9afbc5444 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -1,9 +1,10 @@ -import { ETransactionVersion } from './api'; +import { EDataAvailabilityMode, ETransactionVersion, ResourceBounds } from './api'; import { BigNumberish, BlockIdentifier, V3TransactionDetails } from './lib'; import { DeclareTransactionReceiptResponse, EstimateFeeResponse } from './provider'; export interface EstimateFee extends EstimateFeeResponse { suggestedMaxFee: bigint; + resourceBounds: ResourceBounds; } export type EstimateFeeBulk = Array; @@ -18,6 +19,13 @@ export type AccountInvocationsFactoryDetails = { export interface EstimateFeeDetails { nonce?: BigNumberish; blockIdentifier?: BlockIdentifier; + maxFee?: BigNumberish; // TODO: max_fee is added to match InvocationsDetails + tip?: BigNumberish; + paymasterData?: BigNumberish[]; + accountDeploymentData?: BigNumberish[]; + nonceDataAvailabilityMode?: EDataAvailabilityMode; + feeDataAvailabilityMode?: EDataAvailabilityMode; + version?: BigNumberish; // TODO: this is BigNumberish for interoperability with InvocationsDetails } export interface DeployContractResponse { @@ -54,7 +62,7 @@ export type SimulateTransactionDetails = { blockIdentifier?: BlockIdentifier; skipValidate?: boolean; skipExecute?: boolean; -}; +} & Partial; export enum SIMULATION_FLAG { SKIP_VALIDATE = 'SKIP_VALIDATE', diff --git a/src/utils/stark.ts b/src/utils/stark.ts index d1739362d..0f8791417 100644 --- a/src/utils/stark.ts +++ b/src/utils/stark.ts @@ -1,15 +1,17 @@ import { getStarkKey, utils } from '@scure/starknet'; import { gzip, ungzip } from 'pako'; +import { ZERO } from '../constants'; import { ArraySignatureType, BigNumberish, CompressedProgram, + EstimateFeeDetails, EstimateFeeResponse, Program, Signature, } from '../types'; -import { EDAMode, EDataAvailabilityMode, ResourceBounds } from '../types/api'; +import { EDAMode, EDataAvailabilityMode, ETransactionVersion, ResourceBounds } from '../types/api'; import { addHexPrefix, arrayBufferToString, atobUniversal, btoaUniversal } from './encode'; import { parse, stringify } from './json'; import { @@ -125,3 +127,34 @@ export function intDAM(dam: EDataAvailabilityMode) { if (dam === EDataAvailabilityMode.L2) return EDAMode.L2; throw Error('EDAM conversion'); } + +/** + * Convert to ETransactionVersion or throw an error + * @param defaultVersion ETransactionVersion + * @param providedVersion BigNumberish + * @returns ETransactionVersion + */ +export function toTransactionVersion( + defaultVersion: ETransactionVersion, + providedVersion?: BigNumberish +) { + if (!Object.values(ETransactionVersion).includes(providedVersion as any)) { + throw Error(`${providedVersion} is not supported`); + } + return (providedVersion ? toHex(providedVersion) : defaultVersion) as ETransactionVersion; +} + +/** + * Rerturn provided or default v3 tx details + * @param details EstimateFeeDetails + */ +export function v3Details(details: EstimateFeeDetails) { + return { + tip: details.tip || 0, + paymasterData: details.paymasterData || [], + accountDeploymentData: details.accountDeploymentData || [], + nonceDataAvailabilityMode: details.nonceDataAvailabilityMode || EDataAvailabilityMode.L1, + feeDataAvailabilityMode: details.feeDataAvailabilityMode || EDataAvailabilityMode.L1, + resourceBounds: estimateFeeToBounds(ZERO), + }; +}