diff --git a/CHANGELOG.md b/CHANGELOG.md index 6714ae042fa..46c0eefe645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2728,3 +2728,9 @@ If there are any bugs, improvements, optimizations or any new feature proposal f - The callback function provided to the static `Web3.onNewProviderDiscovered` function expects a parameter of type `EIP6963ProvidersMapUpdateEvent` as opposed to `EIP6963AnnounceProviderEvent`. (#7242) ## [Unreleased] + +### Changed + +#### web3-eth + +- Allow `getEthereumjsTxDataFrom` to return additional fields that may be passed if using a `customTransactionSchema`. diff --git a/packages/web3-eth/CHANGELOG.md b/packages/web3-eth/CHANGELOG.md index e75b0f867b2..71fd65e4f9e 100644 --- a/packages/web3-eth/CHANGELOG.md +++ b/packages/web3-eth/CHANGELOG.md @@ -280,3 +280,7 @@ Documentation: - Adds the same `{transactionSchema?: ValidationSchemaInput}` that exists in `formatTransaction` to `validateTransactionForSigning` ## [Unreleased] + +### Changed + +- Allow `getEthereumjsTxDataFrom` to return additional fields that may be passed if using a `customTransactionSchema`. diff --git a/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts b/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts index cd70e1e265c..a90ce32173d 100644 --- a/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts +++ b/packages/web3-eth/src/utils/prepare_transaction_for_signing.ts @@ -37,6 +37,7 @@ import { transactionBuilder } from './transaction_builder.js'; const getEthereumjsTxDataFromTransaction = ( transaction: FormatType, ) => ({ + ...transaction, nonce: transaction.nonce, gasPrice: transaction.gasPrice, gasLimit: transaction.gasLimit ?? transaction.gas, diff --git a/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts b/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts index ce83a505d17..e0955db8fac 100644 --- a/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts +++ b/packages/web3-eth/test/unit/prepare_transaction_for_signing.test.ts @@ -28,9 +28,13 @@ import { FeeMarketEIP1559Transaction, Transaction, Hardfork, + TypedTransaction, + TransactionFactory, } from 'web3-eth-accounts'; import { prepareTransactionForSigning } from '../../src/utils/prepare_transaction_for_signing'; import { validTransactions } from '../fixtures/prepare_transaction_for_signing'; +import { transactionSchema } from '../../src/schemas'; +import { CustomFieldTransaction } from '../fixtures/format_transaction'; describe('prepareTransactionForSigning', () => { const web3Context = new Web3Context({ @@ -317,4 +321,52 @@ describe('prepareTransactionForSigning', () => { }, ); }); + + it('should not remove extra fields when using a custom schema', async () => { + const context = new Web3Context({ + provider: new HttpProvider('http://127.0.0.1'), + config: { + defaultNetworkId: '0x1', + customTransactionSchema: { + type: 'object', + properties: { + ...transactionSchema.properties, + feeCurrency: { format: 'address' }, + }, + }, + }, + }); + + async function transactionBuilder(options: { + transaction: TransactionType; + web3Context: Web3Context; + privateKey?: HexString | Uint8Array; + fillGasPrice?: boolean; + fillGasLimit?: boolean; + }): Promise { + const tx = { ...options.transaction }; + return tx as unknown as ReturnType; + } + + context.transactionBuilder = transactionBuilder; + + const spy = jest.spyOn(TransactionFactory, 'fromTxData'); + (await prepareTransactionForSigning( + { + chainId: 1458, + nonce: 1, + gasPrice: BigInt(20000000000), + gasLimit: BigInt(21000), + to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + from: '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23', + value: '1000000000', + input: '', + feeCurrency: '0x1234567890123456789012345678901234567890', + } as CustomFieldTransaction, + context, + )) as TypedTransaction & { feeCurrency: string }; + + // @ts-expect-error feeCurrency is a custom field for testing here + expect(spy.mock.lastCall[0].feeCurrency).toBe('0x1234567890123456789012345678901234567890'); + }); }); diff --git a/packages/web3/test/fixtures/tx-type-15/index.ts b/packages/web3/test/fixtures/tx-type-15/index.ts index 8dd34b35e25..1d7dd778067 100644 --- a/packages/web3/test/fixtures/tx-type-15/index.ts +++ b/packages/web3/test/fixtures/tx-type-15/index.ts @@ -32,9 +32,9 @@ import { AccessList, AccessListUint8Array, FeeMarketEIP1559TxData, - FeeMarketEIP1559ValuesArray, JsonTx, TxOptions, + TxValuesArray, } from 'web3-eth-accounts'; const { getAccessListData, getAccessListJSON, getDataFeeEIP2930, verifyAccessList } = txUtils; @@ -43,6 +43,26 @@ const MAX_INTEGER = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffff export const TRANSACTION_TYPE = 15; const TRANSACTION_TYPE_UINT8ARRAY = hexToBytes(TRANSACTION_TYPE.toString(16).padStart(2, '0')); +type CustomFieldTxValuesArray = [ + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + AccessListUint8Array, + Uint8Array, + Uint8Array?, + Uint8Array?, + Uint8Array?, +]; + +type SomeNewTxTypeTxData = FeeMarketEIP1559TxData & { + customTestField: bigint; +}; + /** * Typed transaction with a new gas fee market mechanism * @@ -50,12 +70,13 @@ const TRANSACTION_TYPE_UINT8ARRAY = hexToBytes(TRANSACTION_TYPE.toString(16).pad * - EIP: [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) */ // eslint-disable-next-line no-use-before-define -export class SomeNewTxTypeTransaction extends BaseTransaction { +export class SomeNewTxTypeTransaction extends BaseTransaction { public readonly chainId: bigint; public readonly accessList: AccessListUint8Array; public readonly AccessListJSON: AccessList; public readonly maxPriorityFeePerGas: bigint; public readonly maxFeePerGas: bigint; + public readonly customTestField: bigint; public readonly common: Common; @@ -77,7 +98,7 @@ export class SomeNewTxTypeTransaction extends BaseTransaction. /* eslint-disable @typescript-eslint/no-magic-numbers */ import { Transaction, Web3Account } from 'web3-eth-accounts'; +import { transactionSchema } from 'web3-eth'; import { SupportedProviders, Web3, Web3PluginBase } from '../../src'; import { createAccount, @@ -51,6 +52,14 @@ describe('Add New Tx as a Plugin', () => { }); it('should receive correct type of tx', async () => { web3.registerPlugin(new Eip4844Plugin()); + web3.config.customTransactionSchema = { + type: 'object', + properties: { + ...transactionSchema.properties, + customField: { format: 'string' }, + }, + }; + web3.eth.config.customTransactionSchema = web3.config.customTransactionSchema; const tx = { from: account1.address, to: account2.address, @@ -58,16 +67,21 @@ describe('Add New Tx as a Plugin', () => { type: TRANSACTION_TYPE, maxPriorityFeePerGas: BigInt(5000000), maxFeePerGas: BigInt(5000000), + customField: BigInt(42), }; const sub = web3.eth.sendTransaction(tx); - const waitForEvent: Promise = new Promise(resolve => { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - sub.on('sending', txData => { - resolve(txData as unknown as Transaction); - }); - }); - expect(Number((await waitForEvent).type)).toBe(TRANSACTION_TYPE); + const waitForEvent: Promise = new Promise( + resolve => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + sub.on('sending', txData => { + resolve(txData as unknown as Transaction & { customField: bigint }); + }); + }, + ); + const { type, customField } = await waitForEvent; + expect(Number(type)).toBe(TRANSACTION_TYPE); + expect(BigInt(customField)).toBe(BigInt(42)); await expect(sub).rejects.toThrow(); }); });