Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

100% Coverage web3-utills #7042

Merged
merged 21 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ packages/web3/.in3/
benchmark-data.txt

.eslintcache

.history
37 changes: 23 additions & 14 deletions packages/web3-utils/src/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export const ethUnitMap = {
tether: BigInt('1000000000000000000000000000000'),
};

const PrecisionLossWarning = 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods';
const PrecisionLossWarning =
'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods';

export type EtherUnits = keyof typeof ethUnitMap;
/**
Expand Down Expand Up @@ -366,7 +367,7 @@ export const toHex = (
return returnType ? 'bigint' : numberToHex(value);
}

if(isUint8Array(value)) {
if (isUint8Array(value)) {
return returnType ? 'bytes' : bytesToHex(value);
}

Expand All @@ -386,6 +387,15 @@ export const toHex = (
return returnType ? 'bytes' : `0x${value}`;
}
if (isHex(value) && !isInt(value) && isUInt(value)) {
// This condition seems problematic because meeting
// both conditions `!isInt(value) && isUInt(value)` should be impossible.
// But a value pass for those conditions: "101611154195520776335741463917853444671577865378275924493376429267637792638729"
// Note that according to the docs: it is supposed to be treated as a string (https://docs.web3js.org/guides/web3_upgrade_guide/x/web3_utils_migration_guide#conversion-to-hex)
// In short, the strange is that isInt(value) is false but isUInt(value) is true for the value above.
// TODO: isUInt(value) should be investigated.

// However, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729', true)` is called, it will return `true`.
// But, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729')` is called, it will throw inside `numberToHex`.
return returnType ? 'uint' : numberToHex(value);
}

Expand Down Expand Up @@ -419,14 +429,14 @@ export const toHex = (
*/
export const toNumber = (value: Numbers): number | bigint => {
if (typeof value === 'number') {
if (value > 1e+20) {
console.warn(PrecisionLossWarning)
// JavaScript converts numbers >= 10^21 to scientific notation when coerced to strings,
// leading to potential parsing errors and incorrect representations.
// For instance, String(10000000000000000000000) yields '1e+22'.
// Using BigInt prevents this
return BigInt(value);
}
if (value > 1e20) {
console.warn(PrecisionLossWarning);
// JavaScript converts numbers >= 10^21 to scientific notation when coerced to strings,
// leading to potential parsing errors and incorrect representations.
// For instance, String(10000000000000000000000) yields '1e+22'.
// Using BigInt prevents this
return BigInt(value);
}
return value;
}

Expand Down Expand Up @@ -506,10 +516,9 @@ export const fromWei = (number: Numbers, unit: EtherUnits | number): string => {
if (unit < 0 || !Number.isInteger(unit)) {
throw new InvalidIntegerError(unit);
}
denomination = bigintPower(BigInt(10),BigInt(unit));
denomination = bigintPower(BigInt(10), BigInt(unit));
}


// value in wei would always be integer
// 13456789, 1234
const value = String(toNumber(number));
Expand Down Expand Up @@ -575,8 +584,8 @@ export const toWei = (number: Numbers, unit: EtherUnits | number): string => {
if (unit < 0 || !Number.isInteger(unit)) {
throw new InvalidIntegerError(unit);
}
denomination = bigintPower(BigInt(10),BigInt(unit));

denomination = bigintPower(BigInt(10), BigInt(unit));
}

let parsedNumber = number;
Expand Down
12 changes: 3 additions & 9 deletions packages/web3-utils/src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@

for (const dataPart of dataPath) {
if (result.oneOf && previousDataPath) {
const path = oneOfPath.find(function (element: [string, number]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed this for readability

return (this as unknown as string) === element[0];
}, previousDataPath ?? '');

const currentDataPath = previousDataPath;
const path = oneOfPath.find(([key]) => key === currentDataPath);

Check warning on line 61 in packages/web3-utils/src/formatter.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-utils/src/formatter.ts#L60-L61

Added lines #L60 - L61 were not covered by tests
if (path && path[0] === previousDataPath) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
result = result.oneOf[path[1]];
Expand All @@ -75,10 +73,6 @@
} else if (result.items && (result.items as JsonSchema).properties) {
const node = (result.items as JsonSchema).properties as Record<string, JsonSchema>;

if (!node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we remove this
this conditional can never be met,
} else if (result.items && (result.items as JsonSchema).properties) {
we make sure result.properties always exists

return undefined;
}

result = node[dataPart];
} else if (result.items && isObject(result.items)) {
result = result.items;
Expand Down Expand Up @@ -307,7 +301,7 @@

// If value is an object, recurse into it
if (isObject(value)) {
convert(value, schema, dataPath, format);
convert(value, schema, dataPath, format, oneOfPath);
dataPath.pop();
continue;
}
Expand Down
17 changes: 8 additions & 9 deletions packages/web3-utils/src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.

/**
* This package provides utility functions for Ethereum dapps and other web3.js packages.
*
*
* For using Utils functions, first install Web3 package using `npm i web3` or `yarn add web3`.
* After that, Web3 Utils functions will be available as mentioned below.
* After that, Web3 Utils functions will be available as mentioned below.
* ```ts
* import { Web3 } from 'web3';
* const web3 = new Web3();
*
*
* const value = web3.utils.fromWei("1", "ether")
*
*
* ```
*
*
* For using individual package install `web3-utils` package using `npm i web3-utils` or `yarn add web3-utils` and only import required functions.
* This is more efficient approach for building lightweight applications.
* This is more efficient approach for building lightweight applications.
* ```ts
* import { fromWei, soliditySha3Raw } from 'web3-utils';
*
*
* console.log(fromWei("1", "ether"));
* console.log(soliditySha3Raw({ type: "string", value: "helloworld" }))
*
*
* ```
* @module Utils
*/
Expand Down Expand Up @@ -172,7 +172,6 @@ const getType = (arg: Sha3Input): [string, EncodingTypes] => {
if (Array.isArray(arg)) {
throw new Error('Autodetection of array types is not supported.');
}

let type;
let value;
// if type is given
Expand Down
2 changes: 1 addition & 1 deletion packages/web3-utils/src/json_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ let requestIdSeed: number | undefined;
/**
* Optionally use to make the jsonrpc `id` start from a specific number.
* Without calling this function, the `id` will be filled with a Uuid.
* But after this being called with a number, the `id` will be a number staring from the provided `start` variable.
* But after this being called with a number, the `id` will be a number starting from the provided `start` variable.
* However, if `undefined` was passed to this function, the `id` will be a Uuid again.
* @param start - a number to start incrementing from.
* Or `undefined` to use a new Uuid (this is the default behavior)
Expand Down
39 changes: 19 additions & 20 deletions packages/web3-utils/src/promise_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { isNullish } from 'web3-validator';
export type Timer = ReturnType<typeof setInterval>;
export type Timeout = ReturnType<typeof setTimeout>;


/**
* An alternative to the node function `isPromise` that exists in `util/types` because it is not available on the browser.
* @param object - to check if it is a `Promise`
Expand Down Expand Up @@ -74,7 +73,6 @@ export async function waitWithTimeout<T>(
return result;
}


/**
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached. It returns promise and intervalId.
Expand All @@ -85,25 +83,27 @@ export function pollTillDefinedAndReturnIntervalId<T>(
func: AsyncFunction<T>,
interval: number,
): [Promise<Exclude<T, undefined>>, Timer] {

let intervalId: Timer | undefined;
const polledRes = new Promise<Exclude<T, undefined>>((resolve, reject) => {
intervalId = setInterval(function intervalCallbackFunc(){
(async () => {
try {
const res = await waitWithTimeout(func, interval);

if (!isNullish(res)) {
intervalId = setInterval(
(function intervalCallbackFunc() {
(async () => {
try {
const res = await waitWithTimeout(func, interval);

if (!isNullish(res)) {
clearInterval(intervalId);
resolve(res as unknown as Exclude<T, undefined>);
}
} catch (error) {
clearInterval(intervalId);
resolve(res as unknown as Exclude<T, undefined>);
reject(error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file was changed from prettier

}
} catch (error) {
clearInterval(intervalId);
reject(error);
}
})() as unknown;
return intervalCallbackFunc;}() // this will immediate invoke first call
, interval);
})() as unknown;
return intervalCallbackFunc;
})(), // this will immediate invoke first call
interval,
);
});

return [polledRes as unknown as Promise<Exclude<T, undefined>>, intervalId!];
Expand All @@ -113,7 +113,7 @@ export function pollTillDefinedAndReturnIntervalId<T>(
* Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null),
* or until a timeout is reached.
* pollTillDefinedAndReturnIntervalId() function should be used instead of pollTillDefined if you need IntervalId in result.
* This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId().
* This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId().
* @param func - The function to call.
* @param interval - The interval in milliseconds.
*/
Expand Down Expand Up @@ -146,7 +146,7 @@ export function rejectIfTimeout(timeout: number, error: Error): [Timer, Promise<
/**
* Sets an interval that repeatedly executes the given cond function with the specified interval between each call.
* If the condition is met, the interval is cleared and a Promise that rejects with the returned value is returned.
* @param cond - The function/confition to call.
* @param cond - The function/condition to call.
* @param interval - The interval in milliseconds.
* @returns - an array with the interval ID and the Promise.
*/
Expand All @@ -168,4 +168,3 @@ export function rejectIfConditionAtInterval<T>(
});
return [intervalId!, rejectIfCondition];
}

30 changes: 16 additions & 14 deletions packages/web3-utils/src/socket_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ export abstract class SocketProvider<
protected _validateProviderPath(path: string): boolean {
return !!path;
}

/**
*
* @returns the pendingRequestQueue size
*/
// eslint-disable-next-line class-methods-use-this
public getPendingRequestQueueSize() {
public getPendingRequestQueueSize() {
return this._pendingRequestsQueue.size;
}

Expand Down Expand Up @@ -350,32 +350,34 @@ export abstract class SocketProvider<

/**
* Safely disconnects the socket, async and waits for request size to be 0 before disconnecting
* @param forceDisconnect - If true, will clear queue after 5 attempts of waiting for both pending and sent queue to be 0
* @param forceDisconnect - If true, will clear queue after 5 attempts of waiting for both pending and sent queue to be 0
* @param ms - Determines the ms of setInterval
* @param code - The code to be sent to the server
* @param data - The data to be sent to the server
*/
public async safeDisconnect(code?: number, data?: string, forceDisconnect = false,ms = 1000) {
public async safeDisconnect(code?: number, data?: string, forceDisconnect = false, ms = 1000) {
let retryAttempt = 0;
const checkQueue = async () =>
const checkQueue = async () =>
new Promise(resolve => {
const interval = setInterval(() => {
if (forceDisconnect && retryAttempt === 5) {
if (forceDisconnect && retryAttempt >= 5) {
this.clearQueues();
}
if (this.getPendingRequestQueueSize() === 0 && this.getSentRequestsQueueSize() === 0) {
if (
this.getPendingRequestQueueSize() === 0 &&
this.getSentRequestsQueueSize() === 0
) {
clearInterval(interval);
resolve(true);
}
retryAttempt+=1;
}, ms)
})
retryAttempt += 1;
}, ms);
});

await checkQueue();
this.disconnect(code, data);
}


/**
* Removes all listeners for the specified event type.
* @param type - The event type to remove the listeners for
Expand Down Expand Up @@ -512,7 +514,7 @@ export abstract class SocketProvider<
if (isNullish(responses) || responses.length === 0) {
return;
}

for (const response of responses) {
if (
jsonRpc.isResponseWithNotification(response as JsonRpcNotification) &&
Expand Down Expand Up @@ -544,7 +546,7 @@ export abstract class SocketProvider<
this._sentRequestsQueue.delete(requestId);
}
}

public clearQueues(event?: ConnectionEvent) {
this._clearQueues(event);
}
Expand Down
Loading
Loading