diff --git a/packages/starknet-snap/src/chain/data-client/starkscan.ts b/packages/starknet-snap/src/chain/data-client/starkscan.ts index 8935c80f..ba506f0f 100644 --- a/packages/starknet-snap/src/chain/data-client/starkscan.ts +++ b/packages/starknet-snap/src/chain/data-client/starkscan.ts @@ -1,24 +1,18 @@ import { TransactionType, constants } from 'starknet'; import type { Struct } from 'superstruct'; -import { - ContractFuncName, - TransactionDataVersion, - type Network, - type Transaction, - type TranscationAccountCall, -} from '../../types/snapState'; -import { - TRANSFER_SELECTOR_HEX, - UPGRADE_SELECTOR_HEX, -} from '../../utils/constants'; +import type { V2Transaction } from '../../types/snapState'; +import { type Network, type Transaction } from '../../types/snapState'; import { InvalidNetworkError } from '../../utils/exceptions'; +import { + newDeployTransaction, + newInvokeTransaction, +} from '../../utils/transaction'; import type { HttpHeaders } from '../api-client'; import { ApiClient, HttpMethod } from '../api-client'; import type { IDataClient } from '../data-client'; import type { StarkScanTransactionsResponse } from './starkscan.type'; import { - type StarkScanAccountCall, type StarkScanTransaction, type StarkScanOptions, StarkScanTransactionsResponseStruct, @@ -185,10 +179,6 @@ export class StarkScanClient extends ApiClient implements IDataClient { return tx.transaction_type === TransactionType.DEPLOY_ACCOUNT; } - protected isFundTransferTransaction(entrypoint: string): boolean { - return entrypoint === TRANSFER_SELECTOR_HEX; - } - protected getContractAddress(tx: StarkScanTransaction): string { // backfill the contract address if it is null return tx.contract_address ?? ''; @@ -207,94 +197,64 @@ export class StarkScanClient extends ApiClient implements IDataClient { } protected toTransaction(tx: StarkScanTransaction): Transaction { - /* eslint-disable @typescript-eslint/naming-convention */ + /* eslint-disable @typescript-eslint/naming-convention, camelcase */ const { transaction_hash: txnHash, transaction_type: txnType, timestamp, transaction_finality_status: finalityStatus, transaction_execution_status: executionStatus, - max_fee: maxFee, + max_fee, actual_fee: actualFee, - revert_error: failureReason, + revert_error, + // account_calls representing the calls to invoke from the account contract, it can be multiple + // If the transaction is a deploy transaction, the account_calls is a empty array account_calls: calls, - version, + version: txnVersion, } = tx; - // account_calls representing the calls to invoke from the account contract, it can be multiple - // If the transaction is a deploy transaction, the account_calls is a empty array - const accountCalls = this.toAccountCall(calls); + const { chainId } = this.network; + const senderAddress = this.getSenderAddress(tx); + const failureReason = revert_error ?? ''; + const maxFee = max_fee ?? '0'; + + let transaction: V2Transaction; + + // eslint-disable-next-line no-negated-condition + if (!this.isDeployTransaction(tx)) { + transaction = newInvokeTransaction({ + txnHash, + senderAddress, + chainId, + maxFee, + calls: calls.map((call) => ({ + contractAddress: call.contract_address, + entrypoint: call.selector, + calldata: call.calldata, + })), + txnVersion, + }); + } else { + transaction = newDeployTransaction({ + txnHash, + senderAddress, + chainId, + txnVersion, + }); + } return { - txnHash, - txnType, - chainId: this.network.chainId, - senderAddress: this.getSenderAddress(tx), + ...transaction, + // Override the fields from the StarkScanTransaction timestamp, finalityStatus, executionStatus, - maxFee, actualFee, + maxFee, contractAddress: this.getContractAddress(tx), - accountCalls, - failureReason: failureReason ?? '', - version, - dataVersion: TransactionDataVersion.V2, + failureReason, + txnType, }; - /* eslint-enable */ } - - protected toAccountCall( - accountCalls: StarkScanAccountCall[], - ): Record | null { - if (!accountCalls || accountCalls.length === 0) { - return null; - } - - return accountCalls.reduce( - ( - data: Record, - accountCallArg: StarkScanAccountCall, - ) => { - const { - contract_address: contract, - selector, - calldata: contractCallData, - } = accountCallArg; - - const contractFuncName = this.selectorHexToName(selector); - if (!Object.prototype.hasOwnProperty.call(data, contract)) { - data[contract] = []; - } - - const accountCall: TranscationAccountCall = { - contract, - contractFuncName, - contractCallData, - }; - - if (this.isFundTransferTransaction(selector)) { - accountCall.recipient = accountCallArg.calldata[0]; - accountCall.amount = accountCallArg.calldata[1]; - } - - data[contract].push(accountCall); - - return data; - }, - {}, - ); - } - - protected selectorHexToName(selector: string): string { - switch (selector.toLowerCase()) { - case TRANSFER_SELECTOR_HEX.toLowerCase(): - return ContractFuncName.Transfer; - case UPGRADE_SELECTOR_HEX.toLowerCase(): - return ContractFuncName.Upgrade; - default: - return selector; - } - } } diff --git a/packages/starknet-snap/src/rpcs/execute-txn.test.ts b/packages/starknet-snap/src/rpcs/execute-txn.test.ts index 0a286833..801f2f31 100644 --- a/packages/starknet-snap/src/rpcs/execute-txn.test.ts +++ b/packages/starknet-snap/src/rpcs/execute-txn.test.ts @@ -25,6 +25,8 @@ import * as formatUtils from '../utils/formatter-utils'; import * as starknetUtils from '../utils/starknetUtils'; import { feeTokenToTransactionVersion, + newDeployTransaction as newDeployTransactionFn, + newInvokeTransaction as newInvokeTransactionFn, transactionVersionToFeeToken, transactionVersionToNumber, } from '../utils/transaction'; @@ -627,8 +629,7 @@ describe('ExecuteTxn', () => { calls, } as unknown as SaveDataToStateParamas; - const txnMgr = new TransactionStateManager(true); - const newInvokeTransaction = txnMgr.newInvokeTransaction({ + const newInvokeTransaction = newInvokeTransactionFn({ senderAddress: account.address, txnHash: request.txnHashForExecute, chainId: network.chainId, @@ -636,7 +637,7 @@ describe('ExecuteTxn', () => { txnVersion: transactionVersionToNumber(txnVersion), calls, }); - const newDeployTransaction = txnMgr.newDeployTransaction({ + const newDeployTransaction = newDeployTransactionFn({ senderAddress: account.address, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion txnHash: request.txnHashForDeploy!, diff --git a/packages/starknet-snap/src/rpcs/execute-txn.ts b/packages/starknet-snap/src/rpcs/execute-txn.ts index 3b560f75..3c7714d9 100644 --- a/packages/starknet-snap/src/rpcs/execute-txn.ts +++ b/packages/starknet-snap/src/rpcs/execute-txn.ts @@ -33,6 +33,8 @@ import { transactionVersionToNumber, feeTokenToTransactionVersion, transactionVersionToFeeToken, + newDeployTransaction, + newInvokeTransaction, } from '../utils/transaction'; import type { AccountRpcControllerOptions } from './abstract/account-rpc-controller'; import { AccountRpcController } from './abstract/account-rpc-controller'; @@ -368,7 +370,7 @@ export class ExecuteTxnRpc extends AccountRpcController< if (txnHashForDeploy) { await this.txnStateManager.addTransaction( - this.txnStateManager.newDeployTransaction({ + newDeployTransaction({ senderAddress: address, txnHash: txnHashForDeploy, chainId, @@ -383,7 +385,7 @@ export class ExecuteTxnRpc extends AccountRpcController< } await this.txnStateManager.addTransaction( - this.txnStateManager.newInvokeTransaction({ + newInvokeTransaction({ senderAddress: address, txnHash: txnHashForExecute, chainId, diff --git a/packages/starknet-snap/src/state/transaction-state-manager.ts b/packages/starknet-snap/src/state/transaction-state-manager.ts index 96af6612..0973ce82 100644 --- a/packages/starknet-snap/src/state/transaction-state-manager.ts +++ b/packages/starknet-snap/src/state/transaction-state-manager.ts @@ -1,6 +1,5 @@ -import type { Call, constants } from 'starknet'; +import type { constants, TransactionType } from 'starknet'; import { - TransactionType, TransactionFinalityStatus, TransactionExecutionStatus, } from 'starknet'; @@ -11,7 +10,6 @@ import { TransactionDataVersion, TransactionStatusType, } from '../types/snapState'; -import { msToSec } from '../utils'; import type { IFilter } from './filter'; import { BigIntFilter, @@ -339,105 +337,4 @@ export class TransactionStateManager extends StateManager { throw new StateManagerError(error.message); } } - - /** - * Creates a new transaction object with the given data. - * - * @param params - The parameters of the new transaction object. - * @param params.txnHash - The txn hash. - * @param params.senderAddress - The sender address. - * @param params.chainId - The chain id. - * @param params.maxFee - The max fee. - * @param params.calls - The array of `Call` object. - * @param params.txnVersion - The transaction version. - * @returns The new transaction object. - */ - newInvokeTransaction({ - txnHash, - senderAddress, - chainId, - calls, - txnVersion, - maxFee, - }: { - txnHash: string; - senderAddress: string; - chainId: string; - maxFee: string; - calls: Call[]; - txnVersion: number; - }): V2Transaction { - return { - txnHash, - txnType: TransactionType.INVOKE, - chainId, - senderAddress, - contractAddress: '', - finalityStatus: TransactionFinalityStatus.RECEIVED, - // FIXME: executionStatus will be using the same result as finality if the transaction is yet confirmed - executionStatus: TransactionFinalityStatus.RECEIVED, - failureReason: '', - timestamp: msToSec(Date.now()), - dataVersion: TransactionDataVersion.V2, - version: txnVersion, - maxFee, - // actualFee is always null if the transaction is yet confirmed - actualFee: null, - accountCalls: calls.reduce((acc, callData) => { - const { contractAddress, calldata, entrypoint } = callData; - - if (!Object.prototype.hasOwnProperty.call(acc, contractAddress)) { - acc[contractAddress] = []; - } - acc[contractAddress].push({ - contract: contractAddress, - contractFuncName: entrypoint, - contractCallData: calldata, - }); - - return acc; - }, {}), - }; - } - - /** - * Creates a new transaction object for the deploy account transaction. - * - * @param params - The parameters of the new transaction object. - * @param params.txnHash - The txn hash. - * @param params.senderAddress - The sender address. - * @param params.chainId - The chain id. - * @param params.txnVersion - The transaction version. - * @returns The new transaction object. - */ - newDeployTransaction({ - txnHash, - senderAddress, - chainId, - txnVersion, - }: { - txnHash: string; - senderAddress: string; - chainId: string; - txnVersion: number; - }): V2Transaction { - return { - txnHash, - txnType: TransactionType.DEPLOY_ACCOUNT, - chainId, - senderAddress, - contractAddress: senderAddress, - finalityStatus: TransactionFinalityStatus.RECEIVED, - // FIXME: executionStatus will be using the same result as finality if the transaction is yet confirmed - executionStatus: TransactionFinalityStatus.RECEIVED, - failureReason: '', - timestamp: msToSec(Date.now()), - dataVersion: TransactionDataVersion.V2, - version: txnVersion, - maxFee: null, - // actualFee is always null if the transaction is yet confirmed - actualFee: null, - accountCalls: null, - }; - } } diff --git a/packages/starknet-snap/src/utils/transaction.test.ts b/packages/starknet-snap/src/utils/transaction.test.ts new file mode 100644 index 00000000..daee887e --- /dev/null +++ b/packages/starknet-snap/src/utils/transaction.test.ts @@ -0,0 +1,276 @@ +import { + constants, + TransactionFinalityStatus, + TransactionType, +} from 'starknet'; + +import callsExamples from '../__tests__/fixture/callsExamples.json'; +import { generateAccounts } from '../__tests__/helper'; +import { FeeToken } from '../types/snapApi'; +import { ContractFuncName, TransactionDataVersion } from '../types/snapState'; +import { + ETHER_SEPOLIA_TESTNET, + STRK_SEPOLIA_TESTNET, + TRANSFER_SELECTOR_HEX, + UPGRADE_SELECTOR_HEX, +} from './constants'; +import { + callsToTranscationAccountCalls, + newDeployTransaction, + newInvokeTransaction, + transactionVersionToNumber, + feeTokenToTransactionVersion, + transactionVersionToFeeToken, + transactionSelectorHexToName, + isFundTransferEntrypoint, +} from './transaction'; + +describe('transactionVersionToNumber', () => { + it.each([ + constants.TRANSACTION_VERSION.V3, + constants.TRANSACTION_VERSION.F3, + 3, + '3', + ])( + 'converts the transaction version to 3 if the given txnVersion is %s', + (txnVersion: string) => { + expect(transactionVersionToNumber(txnVersion)).toBe(3); + }, + ); + + it.each([ + ...Object.values(constants.TRANSACTION_VERSION).filter( + (ver) => + ver !== constants.TRANSACTION_VERSION.V3 && + ver !== constants.TRANSACTION_VERSION.F3, + ), + '1', + 1, + 'invalid_version', + ])( + 'converts the transaction version to 1 if the given txnVersion is %s', + (txnVersion: string) => { + expect(transactionVersionToNumber(txnVersion)).toBe(1); + }, + ); +}); + +describe('feeTokenToTransactionVersion', () => { + it('converts feeToken string to transaction version v3 if it is STRK', () => { + expect(feeTokenToTransactionVersion(FeeToken.STRK)).toStrictEqual( + constants.TRANSACTION_VERSION.V3, + ); + }); + + it.each([FeeToken.ETH, 'invalid_unit'])( + 'converts feeToken string to transaction version v1 if it not STRK - %s', + (txnVersion: string) => { + expect(feeTokenToTransactionVersion(txnVersion)).toStrictEqual( + constants.TRANSACTION_VERSION.V1, + ); + }, + ); +}); + +describe('transactionVersionToFeeToken', () => { + it('converts transaction version to STRK unit if it is transaction v3', () => { + expect( + transactionVersionToFeeToken(constants.TRANSACTION_VERSION.V3), + ).toStrictEqual(FeeToken.STRK); + }); + + it.each([ + Object.values(constants.TRANSACTION_VERSION).filter( + (ver) => ver !== constants.TRANSACTION_VERSION.V3, + ), + 'invalid_unit', + ])( + 'converts transaction version to ETH unit if it is not STRK - %s', + (txnVersion: string) => { + expect(transactionVersionToFeeToken(txnVersion)).toStrictEqual( + FeeToken.ETH, + ); + }, + ); +}); + +describe('transactionSelectorHexToName', () => { + it.each([TRANSFER_SELECTOR_HEX, 'transfer'])( + 'converts selector name to `transfer` if it matchs the transfer selector - %s', + (selector: string) => { + expect(transactionSelectorHexToName(selector)).toStrictEqual( + ContractFuncName.Transfer, + ); + }, + ); + + it.each([UPGRADE_SELECTOR_HEX, 'upgrade'])( + 'converts selector name to `upgrade` if it matchs the upgrade selector - %s', + (selector: string) => { + expect(transactionSelectorHexToName(selector)).toStrictEqual( + ContractFuncName.Upgrade, + ); + }, + ); + + it.each(['transfers', 'upgraded', '0x11234'])( + 'returns the original selector string if it doesnt match the hex string for upgrade or transfer', + (selector: string) => { + expect(transactionSelectorHexToName(selector)).toStrictEqual(selector); + }, + ); +}); + +describe('callsToTranscationAccountCalls', () => { + it('converts calls to transaction account calls', () => { + const { calls } = callsExamples.singleCall; + const result = callsToTranscationAccountCalls([calls]); + + const { + contractAddress: contract, + calldata: contractCallData, + entrypoint, + } = calls; + + expect(result).toStrictEqual({ + [contract]: [ + { + contract, + contractCallData, + contractFuncName: transactionSelectorHexToName(entrypoint), + }, + ], + }); + }); + + it('converts calls to transaction account calls with recipient and amount if it is an fund transfer call', async () => { + const [{ address }] = await generateAccounts( + constants.StarknetChainId.SN_SEPOLIA, + 1, + ); + const amount = '100000000000'; + const calls = [ + { + contractAddress: ETHER_SEPOLIA_TESTNET.address, + calldata: [address, amount], + entrypoint: TRANSFER_SELECTOR_HEX, + }, + { + contractAddress: ETHER_SEPOLIA_TESTNET.address, + calldata: [address, amount], + entrypoint: TRANSFER_SELECTOR_HEX, + }, + { + contractAddress: STRK_SEPOLIA_TESTNET.address, + calldata: [address, amount], + entrypoint: TRANSFER_SELECTOR_HEX, + }, + ]; + + const result = callsToTranscationAccountCalls(calls); + + expect(result).toStrictEqual( + calls.reduce((acc, call) => { + const { + contractAddress: contract, + calldata: contractCallData, + entrypoint, + } = call; + + if (!Object.prototype.hasOwnProperty.call(acc, contract)) { + acc[contract] = []; + } + acc[contract].push({ + contract, + contractCallData, + contractFuncName: transactionSelectorHexToName(entrypoint), + recipient: contractCallData[0], + amount: contractCallData[1], + }); + return acc; + }, {}), + ); + }); +}); + +describe('isFundTransferEntrypoint', () => { + it.each([TRANSFER_SELECTOR_HEX, 'transfer'])( + 'returns true if the entrypoint is a fund transfer entrypoint - %s', + (entrypoint: string) => { + expect(isFundTransferEntrypoint(entrypoint)).toBe(true); + }, + ); + + it('returns false if the entrypoint is not a fund transfer entrypoint', () => { + expect(isFundTransferEntrypoint(UPGRADE_SELECTOR_HEX)).toBe(false); + }); +}); + +describe('newInvokeTransaction', () => { + it('creates a new invoke transaction', async () => { + const chainId = constants.StarknetChainId.SN_SEPOLIA; + const [{ address: senderAddress }] = await generateAccounts(chainId, 1); + const { hash: txnHash, calls } = callsExamples.multipleCalls; + const txnVersion = 1; + const maxFee = '10'; + + const result = newInvokeTransaction({ + txnHash, + senderAddress, + chainId, + maxFee, + calls, + txnVersion, + }); + + expect(result).toStrictEqual({ + txnHash, + txnType: TransactionType.INVOKE, + chainId, + senderAddress, + contractAddress: '', + finalityStatus: TransactionFinalityStatus.RECEIVED, + executionStatus: TransactionFinalityStatus.RECEIVED, + failureReason: '', + timestamp: expect.any(Number), + dataVersion: TransactionDataVersion.V2, + version: txnVersion, + maxFee, + actualFee: null, + accountCalls: callsToTranscationAccountCalls(calls), + }); + }); +}); + +describe('newDeployTransaction', () => { + it('creates a new deploy transaction', async () => { + const chainId = constants.StarknetChainId.SN_SEPOLIA; + const [{ address: senderAddress }] = await generateAccounts(chainId, 1); + const { hash: txnHash } = callsExamples.multipleCalls; + const txnVersion = 1; + + const result = newDeployTransaction({ + txnHash, + senderAddress, + chainId, + txnVersion, + }); + + expect(result).toStrictEqual({ + txnHash, + txnType: TransactionType.DEPLOY_ACCOUNT, + chainId, + senderAddress, + contractAddress: senderAddress, + finalityStatus: TransactionFinalityStatus.RECEIVED, + executionStatus: TransactionFinalityStatus.RECEIVED, + failureReason: '', + timestamp: expect.any(Number), + dataVersion: TransactionDataVersion.V2, + version: txnVersion, + maxFee: null, + actualFee: null, + accountCalls: null, + }); + }); +}); diff --git a/packages/starknet-snap/src/utils/transaction.ts b/packages/starknet-snap/src/utils/transaction.ts index 4318833f..7ec2c42f 100644 --- a/packages/starknet-snap/src/utils/transaction.ts +++ b/packages/starknet-snap/src/utils/transaction.ts @@ -1,6 +1,15 @@ -import { constants } from 'starknet'; +import type { Call } from 'starknet'; +import { + constants, + TransactionFinalityStatus, + TransactionType, +} from 'starknet'; import { FeeToken } from '../types/snapApi'; +import type { TranscationAccountCall, V2Transaction } from '../types/snapState'; +import { ContractFuncName, TransactionDataVersion } from '../types/snapState'; +import { TRANSFER_SELECTOR_HEX, UPGRADE_SELECTOR_HEX } from './constants'; +import { msToSec } from './formatter-utils'; /** * Convert the transaction version to number. @@ -8,7 +17,9 @@ import { FeeToken } from '../types/snapApi'; * @param txnVersion - The transaction version. * @returns The transaction version number. */ -export function transactionVersionToNumber(txnVersion: string): number { +export function transactionVersionToNumber( + txnVersion: string | number, +): number { const v3TxnVersion = new Set([ constants.TRANSACTION_VERSION.V3, constants.TRANSACTION_VERSION.F3, @@ -43,3 +54,163 @@ export function transactionVersionToFeeToken(txnVersion: string): FeeToken { ? FeeToken.STRK : FeeToken.ETH; } + +/** + * Convert the transaction selector to string name. + * If the selector is not known, return the selector. + * + * @param selector - The transaction selector. + * @returns The meaninful name of the selector if it is known, otherwise return the selector. + */ +export function transactionSelectorHexToName(selector: string): string { + switch (selector.toLowerCase()) { + case ContractFuncName.Transfer.toLowerCase(): + case TRANSFER_SELECTOR_HEX.toLowerCase(): + return ContractFuncName.Transfer; + case ContractFuncName.Upgrade.toLowerCase(): + case UPGRADE_SELECTOR_HEX.toLowerCase(): + return ContractFuncName.Upgrade; + default: + return selector; + } +} + +/** + * Convert an array of `Call` objects to a record of `TranscationAccountCall` objects. + * + * @param calls - The array of `Call` object. + * @returns The record of `TranscationAccountCall` objects. + */ +export function callsToTranscationAccountCalls( + calls: Call[], +): Record { + return calls.reduce((acc, call) => { + const { + contractAddress: contract, + calldata: contractCallData, + entrypoint, + } = call; + + const contractFuncName = transactionSelectorHexToName(entrypoint); + + if (!Object.prototype.hasOwnProperty.call(acc, contract)) { + acc[contract] = []; + } + + const accountCall: TranscationAccountCall = { + contract, + contractFuncName, + contractCallData: contractCallData as unknown as string[], + }; + + if (isFundTransferEntrypoint(entrypoint)) { + accountCall.recipient = accountCall.contractCallData[0]; + accountCall.amount = accountCall.contractCallData[1]; + } + + acc[contract].push(accountCall); + + return acc; + }, {}); +} + +/** + * Check if the entrypoint is a fund transfer entrypoint. + * + * @param entrypoint - The entrypoint. + * @returns `true` if the entrypoint is a fund transfer entrypoint, otherwise `false`. + */ +export function isFundTransferEntrypoint(entrypoint: string): boolean { + return ( + entrypoint.toLowerCase() === TRANSFER_SELECTOR_HEX || + entrypoint.toLowerCase() === ContractFuncName.Transfer + ); +} + +/** + * Creates a new transaction object with the given data. + * + * @param params - The parameters of the new transaction object. + * @param params.txnHash - The txn hash. + * @param params.senderAddress - The sender address. + * @param params.chainId - The chain id. + * @param params.maxFee - The max fee. + * @param params.calls - The array of `Call` object. + * @param params.txnVersion - The transaction version. + * @returns The new transaction object. + */ +export function newInvokeTransaction({ + txnHash, + senderAddress, + chainId, + calls, + txnVersion, + maxFee, +}: { + txnHash: string; + senderAddress: string; + chainId: string; + maxFee: string; + calls: Call[]; + txnVersion: number; +}): V2Transaction { + return { + txnHash, + txnType: TransactionType.INVOKE, + chainId, + senderAddress, + contractAddress: '', + finalityStatus: TransactionFinalityStatus.RECEIVED, + // executionStatus will be using the same result as finality if the transaction is yet confirmed + executionStatus: TransactionFinalityStatus.RECEIVED, + failureReason: '', + timestamp: msToSec(Date.now()), + dataVersion: TransactionDataVersion.V2, + version: txnVersion, + maxFee, + // actualFee is always null if the transaction is yet confirmed + actualFee: null, + accountCalls: callsToTranscationAccountCalls(calls), + }; +} + +/** + * Creates a new transaction object for the deploy account transaction. + * + * @param params - The parameters of the new transaction object. + * @param params.txnHash - The txn hash. + * @param params.senderAddress - The sender address. + * @param params.chainId - The chain id. + * @param params.txnVersion - The transaction version. + * @returns The new transaction object. + */ +export function newDeployTransaction({ + txnHash, + senderAddress, + chainId, + txnVersion, +}: { + txnHash: string; + senderAddress: string; + chainId: string; + txnVersion: number; +}): V2Transaction { + return { + txnHash, + txnType: TransactionType.DEPLOY_ACCOUNT, + chainId, + senderAddress, + contractAddress: senderAddress, + finalityStatus: TransactionFinalityStatus.RECEIVED, + // executionStatus will be using the same result as finality if the transaction is yet confirmed + executionStatus: TransactionFinalityStatus.RECEIVED, + failureReason: '', + timestamp: msToSec(Date.now()), + dataVersion: TransactionDataVersion.V2, + version: txnVersion, + maxFee: null, + // actualFee is always null if the transaction is yet confirmed + actualFee: null, + accountCalls: null, + }; +}