Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit edc7a84

Browse files
defaultTransactionTypeParser Refactor (#6102)
1 parent ed5a7f8 commit edc7a84

File tree

23 files changed

+560
-106
lines changed

23 files changed

+560
-106
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,12 @@ should use 4.0.1-alpha.0 for testing.
14751475
#### web3-types
14761476

14771477
- Added `filters` param to the `Filter` type (#6010)
1478+
- Export for `HardforksOrdered` enum (#6102)
1479+
- Export for `Web3ValidationErrorObject` type (#6102)
1480+
1481+
#### web3-errors
1482+
1483+
- `InvalidPropertiesForTransactionTypeError` with error code `429` (#6102)
14781484

14791485
### Fixed
14801486

@@ -1488,3 +1494,10 @@ should use 4.0.1-alpha.0 for testing.
14881494

14891495
- `formatTransaction` no longer throws a `TransactionDataAndInputError` if it's passed a transaction object with both `data` and `input` properties set (as long as they are the same value) (#6064)
14901496
- Refactored documentation for `rpc_method_wrappers` to point to the previously duplicated documentation found under the `Web3Eth` class documentation (#6054)
1497+
- Refactored `defaultTransactionTypeParser` to return correct EIP-2718 types, prior implementation was prioritizing `transaction.hardfork` and ignoring the use of `transaction.gasLimit`. `defaultTransactionTypeParser` will now throw `InvalidPropertiesForTransactionTypeError`s for properties are used that are incompatible with `transaction.type` (#6102)
1498+
1499+
### Removed
1500+
1501+
#### web3-validator
1502+
1503+
- `Web3ValidationErrorObject` type is now exported from `web3-types` package (#6102)

packages/web3-errors/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9999

100100
## [Unreleased]
101101

102+
### Added
103+
104+
- `InvalidPropertiesForTransactionTypeError` with error code `429` (#6102)
105+
102106
### Changed
103107

104108
- Nested Smart Contract error data is extracted at `Eip838ExecutionError` constructor and the nested error is set at `innerError` (#6045)

packages/web3-errors/src/error_codes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export const ERR_TX_CHAIN_MISMATCH = 435;
8888
export const ERR_TX_HARDFORK_MISMATCH = 436;
8989
export const ERR_TX_INVALID_RECEIVER = 437;
9090
export const ERR_TX_REVERT_TRANSACTION_CUSTOM_ERROR = 438;
91+
export const ERR_TX_INVALID_PROPERTIES_FOR_TYPE = 439;
9192

9293
// Connection error codes
9394
export const ERR_CONN = 500;

packages/web3-errors/src/errors/transaction_errors.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
1717

1818
/* eslint-disable max-classes-per-file */
1919

20-
import { Bytes, HexString, Numbers, TransactionReceipt } from 'web3-types';
20+
import {
21+
Bytes,
22+
HexString,
23+
Numbers,
24+
TransactionReceipt,
25+
Web3ValidationErrorObject,
26+
} from 'web3-types';
2127
import {
2228
ERR_RAW_TX_UNDEFINED,
2329
ERR_TX,
@@ -58,6 +64,7 @@ import {
5864
ERR_TX_UNSUPPORTED_EIP_1559,
5965
ERR_TX_UNSUPPORTED_TYPE,
6066
ERR_TX_REVERT_TRANSACTION_CUSTOM_ERROR,
67+
ERR_TX_INVALID_PROPERTIES_FOR_TYPE,
6168
} from '../error_codes';
6269
import { InvalidValueError, BaseWeb3Error } from '../web3_error_base';
6370

@@ -540,3 +547,26 @@ export class LocalWalletNotAvailableError extends InvalidValueError {
540547
);
541548
}
542549
}
550+
export class InvalidPropertiesForTransactionTypeError extends BaseWeb3Error {
551+
public code = ERR_TX_INVALID_PROPERTIES_FOR_TYPE;
552+
553+
public constructor(
554+
validationError: Web3ValidationErrorObject[],
555+
txType: '0x0' | '0x1' | '0x2',
556+
) {
557+
const invalidPropertyNames: string[] = [];
558+
validationError.forEach(error =>
559+
invalidPropertyNames.push(
560+
// These errors are erroneously reported, error
561+
// has type Web3ValidationErrorObject, but eslint doesn't recognize it
562+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
563+
(error.keyword.match(/data.(.+)/) as string[])[1],
564+
),
565+
);
566+
super(
567+
`The following properties are invalid for the transaction type ${txType}: ${invalidPropertyNames.join(
568+
', ',
569+
)}`,
570+
);
571+
}
572+
}

packages/web3-errors/test/unit/__snapshots__/errors.test.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ Object {
166166
}
167167
`;
168168

169+
exports[`errors InvalidPropertiesForTransactionTypeError should have valid json structure 1`] = `
170+
Object {
171+
"code": 439,
172+
"innerError": undefined,
173+
"message": "The following properties are invalid for the transaction type 0x0: property",
174+
"name": "InvalidPropertiesForTransactionTypeError",
175+
}
176+
`;
177+
169178
exports[`errors InvalidProviderError should have valid json structure 1`] = `
170179
Object {
171180
"code": 601,

packages/web3-errors/test/unit/errors.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ describe('errors', () => {
5151
...transactionErrors,
5252
...utilsErrors,
5353
})) {
54+
if (ErrorClass === transactionErrors.InvalidPropertiesForTransactionTypeError) break;
5455
// To disable error for the abstract class
5556

5657
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -191,6 +192,24 @@ describe('errors', () => {
191192
});
192193
});
193194

195+
describe('InvalidPropertiesForTransactionTypeError', () => {
196+
it('should have valid json structure', () => {
197+
expect(
198+
new transactionErrors.InvalidPropertiesForTransactionTypeError(
199+
[
200+
{
201+
keyword: 'data.property',
202+
instancePath: '',
203+
schemaPath: '',
204+
params: {},
205+
},
206+
],
207+
'0x0',
208+
).toJSON(),
209+
).toMatchSnapshot();
210+
});
211+
});
212+
194213
describe('NoContractAddressFoundError', () => {
195214
it('should have valid json structure', () => {
196215
expect(

packages/web3-eth-contract/src/contract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
DataFormat,
7070
DEFAULT_RETURN_FORMAT,
7171
Numbers,
72+
Web3ValidationErrorObject,
7273
} from 'web3-types';
7374
import { format, isDataFormat, toChecksumAddress } from 'web3-utils';
7475
import {
@@ -77,7 +78,6 @@ import {
7778
utils as validatorUtils,
7879
ValidationSchemaInput,
7980
Web3ValidatorError,
80-
Web3ValidationErrorObject,
8181
} from 'web3-validator';
8282
import { ALL_EVENTS_ABI } from './constants';
8383
import { decodeEventABI, decodeMethodReturn, encodeEventABI, encodeMethodABI } from './encoding';

packages/web3-eth/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
131131
- `formatTransaction` no longer throws a `TransactionDataAndInputError` if it's passed a transaction object with both `data` and `input` properties set (as long as they are the same value) (#6064)
132132
- Refactored documentation for `rpc_method_wrappers` to point to the previously duplicated documentation found under the `Web3Eth` class documentation (#6054)
133133
- Replaced Buffer for Uint8Array (#6004)
134+
- Refactored `defaultTransactionTypeParser` to return correct EIP-2718 types, prior implementation was prioritizing `transaction.hardfork` and ignoring the use of `transaction.gasLimit`. `defaultTransactionTypeParser` will now throw `InvalidPropertiesForTransactionTypeError`s for properties are used that are incompatible with `transaction.type` (#6102)

packages/web3-eth/src/utils/detect_transaction_type.ts

Lines changed: 111 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,127 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
1717

1818
import { format, toHex } from 'web3-utils';
1919
import { TransactionTypeParser, Web3Context } from 'web3-core';
20-
import { EthExecutionAPI, Transaction, ETH_DATA_FORMAT } from 'web3-types';
21-
import { isNullish } from 'web3-validator';
20+
import { EthExecutionAPI, HardforksOrdered, Transaction, ETH_DATA_FORMAT } from 'web3-types';
21+
import { Web3ValidatorError, isNullish, validator } from 'web3-validator';
22+
import { InvalidPropertiesForTransactionTypeError } from 'web3-errors';
23+
2224
import { InternalTransaction } from '../types';
2325

26+
// undefined is treated as null for JSON schema validator
27+
const transactionType0x0Schema = {
28+
type: 'object',
29+
properties: {
30+
accessList: {
31+
type: 'null',
32+
},
33+
maxFeePerGas: {
34+
type: 'null',
35+
},
36+
maxPriorityFeePerGas: {
37+
type: 'null',
38+
},
39+
},
40+
};
41+
const transactionType0x1Schema = {
42+
type: 'object',
43+
properties: {
44+
maxFeePerGas: {
45+
type: 'null',
46+
},
47+
maxPriorityFeePerGas: {
48+
type: 'null',
49+
},
50+
},
51+
};
52+
const transactionType0x2Schema = {
53+
type: 'object',
54+
properties: {
55+
gasPrice: {
56+
type: 'null',
57+
},
58+
},
59+
};
60+
61+
const validateTxTypeAndHandleErrors = (
62+
txSchema: object,
63+
tx: Transaction,
64+
txType: '0x0' | '0x1' | '0x2',
65+
) => {
66+
try {
67+
validator.validateJSONSchema(txSchema, tx);
68+
} catch (error) {
69+
if (error instanceof Web3ValidatorError)
70+
// Erroneously reported error
71+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
72+
throw new InvalidPropertiesForTransactionTypeError(error.errors, txType);
73+
74+
throw error;
75+
}
76+
};
77+
2478
export const defaultTransactionTypeParser: TransactionTypeParser = transaction => {
2579
const tx = transaction as unknown as Transaction;
2680

27-
if (!isNullish(tx.type)) return format({ format: 'uint' }, tx.type, ETH_DATA_FORMAT);
81+
if (!isNullish(tx.type)) {
82+
let txSchema;
83+
switch (tx.type) {
84+
case '0x0':
85+
txSchema = transactionType0x0Schema;
86+
break;
87+
case '0x1':
88+
txSchema = transactionType0x1Schema;
89+
break;
90+
case '0x2':
91+
txSchema = transactionType0x2Schema;
92+
break;
93+
94+
default:
95+
return format({ format: 'uint' }, tx.type, ETH_DATA_FORMAT);
96+
}
2897

29-
if (
30-
!isNullish(tx.maxFeePerGas) ||
31-
!isNullish(tx.maxPriorityFeePerGas) ||
32-
tx.hardfork === 'london' ||
33-
tx.common?.hardfork === 'london'
34-
)
98+
validateTxTypeAndHandleErrors(txSchema, tx, tx.type);
99+
100+
return format({ format: 'uint' }, tx.type, ETH_DATA_FORMAT);
101+
}
102+
103+
if (!isNullish(tx.maxFeePerGas) || !isNullish(tx.maxPriorityFeePerGas)) {
104+
validateTxTypeAndHandleErrors(transactionType0x2Schema, tx, '0x2');
35105
return '0x2';
106+
}
36107

37-
if (!isNullish(tx.accessList) || tx.hardfork === 'berlin' || tx.common?.hardfork === 'berlin')
108+
if (!isNullish(tx.accessList)) {
109+
validateTxTypeAndHandleErrors(transactionType0x1Schema, tx, '0x1');
38110
return '0x1';
111+
}
112+
113+
// We don't return 0x0 here, because if gasPrice is not
114+
// undefined, we still don't know if the network
115+
// supports EIP-2718 (https://eips.ethereum.org/EIPS/eip-2718)
116+
// and whether we should return undefined for legacy txs,
117+
// or type 0x0 for legacy txs post EIP-2718
118+
if (!isNullish(tx.gasPrice)) {
119+
validateTxTypeAndHandleErrors(transactionType0x0Schema, tx, '0x0');
120+
}
121+
122+
const givenHardfork = tx.hardfork ?? tx.common?.hardfork;
123+
// If we don't have a hardfork, then we can't be sure we're post
124+
// EIP-2718 where transaction types are available
125+
if (givenHardfork === undefined) return undefined;
126+
127+
const hardforkIndex = Object.keys(HardforksOrdered).indexOf(givenHardfork);
128+
129+
// Unknown hardfork
130+
if (hardforkIndex === undefined) return undefined;
131+
132+
// givenHardfork is London or later, so EIP-2718 is supported
133+
if (hardforkIndex >= Object.keys(HardforksOrdered).indexOf('london'))
134+
return !isNullish(tx.gasPrice) ? '0x0' : '0x2';
135+
136+
// givenHardfork is Berlin, tx.accessList is undefined, assume type is 0x0
137+
if (hardforkIndex === Object.keys(HardforksOrdered).indexOf('berlin')) return '0x0';
39138

139+
// For all pre-Berlin hardforks, return undefined since EIP-2718
140+
// isn't supported
40141
return undefined;
41142
};
42143

0 commit comments

Comments
 (0)