Skip to content

Commit

Permalink
Merge branch '4.x' into 6371-uncaught-typeerror-class-extends-value-u…
Browse files Browse the repository at this point in the history
…ndefined-is-not-a-constructor-or-null
  • Loading branch information
Muhammad-Altabba authored Oct 4, 2023
2 parents 6d0bd23 + 90d78c1 commit cb742c7
Show file tree
Hide file tree
Showing 74 changed files with 89,140 additions and 175 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"buffer": "^6.0.3",
"bufferutil": "^4.0.6",
"clean-webpack-plugin": "^4.0.0",
"concurrently": "^8.2.0",
"cypress-jest-adapter": "^0.1.1",
"declaration-bundler-webpack-plugin": "^1.0.3",
"eslint": "^8.20.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/web3-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,5 @@ Documentation:

### Changed

- defaultTransactionType is now type 0x2 instead of 0x0 (#6282)
- defaultTransactionType is now type 0x2 instead of 0x0 (#6282)
- Allows formatter to parse large base fee (#6456)
2 changes: 1 addition & 1 deletion packages/web3-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"clean": "rimraf dist && rimraf lib",
"prebuild": "yarn clean",
"build": "yarn build:cjs & yarn build:esm & yarn build:types",
"build": "concurrently --kill-others-on-fail \"yarn:build:*(!check)\"",
"build:cjs": "tsc --build tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > ./lib/commonjs/package.json",
"build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json",
"build:types": "tsc --build tsconfig.types.json",
Expand Down
2 changes: 1 addition & 1 deletion packages/web3-core/src/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ export const outputBlockFormatter = (block: BlockInput): BlockOutput => {
}

if (block.baseFeePerGas) {
modifiedBlock.baseFeePerGas = hexToNumber(block.baseFeePerGas);
modifiedBlock.baseFeePerGas = outputBigIntegerFormatter(block.baseFeePerGas);
}

return modifiedBlock;
Expand Down
3 changes: 2 additions & 1 deletion packages/web3-core/test/unit/formatters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,13 @@ describe('formatters', () => {
});

it('should convert "baseFeePerGas" from hex to number', () => {
jest.spyOn(formatters, 'outputBigIntegerFormatter').mockReturnValue(123);
const result = outputBlockFormatter({
...validBlock,
baseFeePerGas: 'baseFeePerGas',
} as any);

expect(utils.hexToNumber).toHaveBeenCalledWith('baseFeePerGas');
expect(outputBigIntegerFormatter).toHaveBeenCalledWith('baseFeePerGas');
expect(result).toEqual(expect.objectContaining({ baseFeePerGas: hexToNumberResult }));
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/web3-core/test/unit/web3_batch_request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { JsonRpcBatchRequest, JsonRpcBatchResponse, JsonRpcOptionalRequest } from 'web3-types';
import { jsonRpc, Web3DeferredPromise } from 'web3-utils';
import { jsonRpc, Web3DeferredPromise, Timeout } from 'web3-utils';
import { OperationAbortError, OperationTimeoutError } from 'web3-errors';
import { Web3BatchRequest } from '../../src/web3_batch_request';

Expand Down Expand Up @@ -174,7 +174,7 @@ describe('Web3BatchRequest', () => {
});

it('should timeout if request not executed in a particular time', async () => {
let timerId!: NodeJS.Timeout;
let timerId!: Timeout;

jest.spyOn(requestManager, 'sendBatch').mockImplementation(async () => {
return new Promise(resolve => {
Expand Down
2 changes: 1 addition & 1 deletion packages/web3-errors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"clean": "rimraf dist && rimraf lib",
"prebuild": "yarn clean",
"build": "yarn build:cjs & yarn build:esm & yarn build:types",
"build": "concurrently --kill-others-on-fail \"yarn:build:*(!check)\"",
"build:cjs": "tsc --build tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > ./lib/commonjs/package.json",
"build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json",
"build:types": "tsc --build tsconfig.types.json",
Expand Down
6 changes: 6 additions & 0 deletions packages/web3-errors/src/errors/generic_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export class OperationAbortError extends BaseWeb3Error {

export class AbiError extends BaseWeb3Error {
public code = ERR_ABI_ENCODING;
public readonly props: Record<string, unknown> & { name?: string };

public constructor(message: string, props?: Record<string, unknown> & { name?: string }) {
super(message);
this.props = props ?? {};
}
}

export class ExistingPluginNamespaceError extends BaseWeb3Error {
Expand Down
6 changes: 3 additions & 3 deletions packages/web3-eth-abi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"clean": "rimraf dist && rimraf lib",
"prebuild": "yarn clean",
"build": "yarn build:cjs & yarn build:esm & yarn build:types",
"build": "concurrently --kill-others-on-fail \"yarn:build:*(!check)\"",
"build:cjs": "tsc --build tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > ./lib/commonjs/package.json",
"build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json",
"build:types": "tsc --build tsconfig.types.json",
Expand All @@ -42,8 +42,8 @@
"test:integration": "jest --config=./test/integration/jest.config.js --passWithNoTests"
},
"dependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"abitype": "0.7.1",
"web3-validator": "^2.0.2",
"web3-errors": "^1.1.2",
"web3-types": "^1.2.0",
"web3-utils": "^4.0.6"
Expand Down
79 changes: 9 additions & 70 deletions packages/web3-eth-abi/src/api/parameters_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { AbiError } from 'web3-errors';
import { ParamType, Result } from '@ethersproject/abi';
import { HexString, AbiInput, DecodedParams } from 'web3-types';
import ethersAbiCoder from '../ethers_abi_coder.js';
import { formatParam, isAbiFragment, mapTypes, modifyParams } from '../utils.js';
import { AbiInput, HexString } from 'web3-types';
import { decodeParameters as decodeParametersInternal } from '../coders/decode.js';
import { encodeParameters as encodeParametersInternal } from '../coders/encode.js';

/**
* Encodes a parameter based on its type to its ABI representation.
Expand All @@ -37,40 +35,8 @@ import { formatParam, isAbiFragment, mapTypes, modifyParams } from '../utils.js'
* > 0x000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000
* ```
*/
export const encodeParameters = (abi: ReadonlyArray<AbiInput>, params: unknown[]): string => {
try {
const modifiedTypes = mapTypes(
Array.isArray(abi) ? (abi as AbiInput[]) : ([abi] as unknown as AbiInput[]),
);
const modifiedParams: Array<unknown> = [];
for (const [index, param] of params.entries()) {
const item = modifiedTypes[index];
let type: string;

if (isAbiFragment(item) && item.type) {
// We may get a named type of shape {name, type}
type = item.type;
} else {
type = item as unknown as string;
}

const newParam = formatParam(type, param);

if (typeof type === 'string' && type.includes('tuple')) {
const coder = ethersAbiCoder._getCoder(ParamType.from(type));
modifyParams(coder, [newParam]);
}

modifiedParams.push(newParam);
}
return ethersAbiCoder.encode(
modifiedTypes.map(p => ParamType.from(p)),
modifiedParams,
);
} catch (err) {
throw new AbiError(`Parameter encoding error`, err as Error);
}
};
export const encodeParameters = (abi: ReadonlyArray<AbiInput>, params: unknown[]): string =>
encodeParametersInternal(abi, params);

/**
* Encodes a parameter based on its type to its ABI representation.
Expand Down Expand Up @@ -130,30 +96,6 @@ export const encodeParameters = (abi: ReadonlyArray<AbiInput>, params: unknown[]
*/
export const encodeParameter = (abi: AbiInput, param: unknown): string =>
encodeParameters([abi], [param]);

// If encoded param is an array and there are mixed on integer and string keys
const isParamRequiredToConvert = (data: Result): boolean =>
Array.isArray(data) &&
Object.keys(data).filter(k => Number.isInteger(+k)).length !== Object.keys(data).length;

// Ethers-Encoder return the decoded result as an array with additional string indexes for named params
// We want these to be converted to an object with named keys
const formatArrayResToObject = (data: Result): DecodedParams => {
const returnValue: DecodedParams = {
__length__: 0,
};

for (const key of Object.keys(data)) {
returnValue[key] =
Array.isArray(data[key]) && isParamRequiredToConvert(data[key] as Result)
? formatArrayResToObject(data[key] as Result)
: data[key];

returnValue.__length__ += Number.isInteger(+key) ? 1 : 0;
}
return returnValue;
};

/**
* Should be used to decode list of params
*/
Expand All @@ -172,14 +114,11 @@ export const decodeParametersWith = (
'or querying a node which is not fully synced.',
);
}
const res = ethersAbiCoder.decode(
mapTypes(abis).map(p => ParamType.from(p)),
`0x${bytes.replace(/0x/i, '')}`,
loose,
);
return formatArrayResToObject(res);
return decodeParametersInternal(abis, `0x${bytes.replace(/0x/i, '')}`, loose);
} catch (err) {
throw new AbiError(`Parameter decoding error: ${(err as Error).message}`);
throw new AbiError(`Parameter decoding error: ${(err as Error).message}`, {
internalErr: err,
});
}
};

Expand Down
75 changes: 75 additions & 0 deletions packages/web3-eth-abi/src/coders/base/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { AbiError } from 'web3-errors';
import { AbiParameter } from 'web3-types';
import { toChecksumAddress } from 'web3-utils';
import { isAddress, utils } from 'web3-validator';
import { DecoderResult, EncoderResult } from '../types.js';
import { alloc, WORD_SIZE } from '../utils.js';

const ADDRESS_BYTES_COUNT = 20;
const ADDRESS_OFFSET = WORD_SIZE - ADDRESS_BYTES_COUNT;

export function encodeAddress(param: AbiParameter, input: unknown): EncoderResult {
if (typeof input !== 'string') {
throw new AbiError('address type expects string as input type', {
value: input,
name: param.name,
type: param.type,
});
}
let address = input.toLowerCase();
if (!address.startsWith('0x')) {
address = `0x${address}`;
}
if (!isAddress(address)) {
throw new AbiError('provided input is not valid address', {
value: input,
name: param.name,
type: param.type,
});
}
// for better performance, we could convert hex to destination bytes directly (encoded var)
const addressBytes = utils.hexToUint8Array(address);
// expand address to WORD_SIZE
const encoded = alloc(WORD_SIZE);
encoded.set(addressBytes, ADDRESS_OFFSET);
return {
dynamic: false,
encoded,
};
}

export function decodeAddress(_param: AbiParameter, bytes: Uint8Array): DecoderResult<string> {
const addressBytes = bytes.subarray(ADDRESS_OFFSET, WORD_SIZE);
if (addressBytes.length !== ADDRESS_BYTES_COUNT) {
throw new AbiError('Invalid decoding input, not enough bytes to decode address', { bytes });
}
const result = utils.uint8ArrayToHexString(addressBytes);

// should we check is decoded value is valid address?
// if(!isAddress(result)) {
// throw new AbiError("encoded data is not valid address", {
// address: result,
// });
// }
return {
result: toChecksumAddress(result),
encoded: bytes.subarray(WORD_SIZE),
consumed: WORD_SIZE,
};
}
Loading

0 comments on commit cb742c7

Please sign in to comment.