DEV Community

rahuldev-17
rahuldev-17

Posted on

How to go deep in ethers one function at a time: ParseUnits()

Time to get familiar with your tool-set. Okay, so how does Ricmoo write his js/ts functions? Let us see ParseUnits().

It is written in Typescript at ethers.js/packages/units/src.ts/index.ts. Here it goes:

export function parseUnits(value: string, unitName?: BigNumberish): BigNumber {
    if (typeof(value) !== "string") {
        logger.throwArgumentError("value must be a string", "value", value);
    }
    if (typeof(unitName) === "string") {
        const index = names.indexOf(unitName);
        if (index !== -1) { unitName = 3 * index; }
    }
    return parseFixed(value, (unitName != null) ? unitName: 18);
}
Enter fullscreen mode Exit fullscreen mode

The function takes two arguements and one is strigified value, and another 'unitname'. This function is kind of checker, meat is in 'parseFixed' function. And we see that this function is taken from:

import { formatFixed, parseFixed } from "@ethersproject/bignumber";

From 'bignumber', we get to know it is coming from 'fixednumber':

export { formatFixed, FixedFormat, FixedNumber, parseFixed } from "./fixednumber";

export function parseFixed(value: string, decimals?: BigNumberish): BigNumber {

    if (decimals == null) { decimals = 0; }
    const multiplier = getMultiplier(decimals);

    if (typeof(value) !== "string" || !value.match(/^-?[0-9.]+$/)) {
        logger.throwArgumentError("invalid decimal value", "value", value);
    }

    // Is it negative?
    const negative = (value.substring(0, 1) === "-");
    if (negative) { value = value.substring(1); }

    if (value === ".") {
        logger.throwArgumentError("missing value", "value", value);
    }

    // Split it into a whole and fractional part
    const comps = value.split(".");
    if (comps.length > 2) {
        logger.throwArgumentError("too many decimal points", "value", value);
    }

    let whole = comps[0], fraction = comps[1];
    if (!whole) { whole = "0"; }
    if (!fraction) { fraction = "0"; }

    // Trim trailing zeros
    while (fraction[fraction.length - 1] === "0") {
        fraction = fraction.substring(0, fraction.length - 1);
    }

    // Check the fraction doesn't exceed our decimals size
    if (fraction.length > multiplier.length - 1) {
        throwFault("fractional component exceeds decimals", "underflow", "parseFixed");
    }

    // If decimals is 0, we have an empty string for fraction
    if (fraction === "") { fraction = "0"; }

    // Fully pad the string with zeros to get to wei
    while (fraction.length < multiplier.length - 1) { fraction += "0"; }

    const wholeValue = BigNumber.from(whole);
    const fractionValue = BigNumber.from(fraction);

    let wei = (wholeValue.mul(multiplier)).add(fractionValue);

    if (negative) { wei = wei.mul(NegativeOne); }

    return wei;
}

Enter fullscreen mode Exit fullscreen mode

Hope now you understand where is the real body after all the check. If no, here it is:

const wholeValue = BigNumber.from(whole);
    const fractionValue = BigNumber.from(fraction);

    let wei = (wholeValue.mul(multiplier)).add(fractionValue);

    if (negative) { wei = wei.mul(NegativeOne); }

    return wei;
Enter fullscreen mode Exit fullscreen mode

Basically, Bignumber library is used to convert whole and fraction parts of value into into 'bignumber', and then they are added. So all ether calculation is 'wei' in ethers.

But from where the 'Bignumber' is coming? It is coming from:

import { BigNumber, BigNumberish, isBigNumberish } from "./bignumber";
Enter fullscreen mode Exit fullscreen mode

Lol, it is coming from 'bignumber'.

From there, we get to know it is wrapper around 'BN.js' library:

/**
 *  BigNumber
 *
 *  A wrapper around the BN.js object. We use the BN.js library
 *  because it is used by elliptic, so it is required regardless.
 *
 */

import _BN from "bn.js";
import BN = _BN.BN;
Enter fullscreen mode Exit fullscreen mode

Do you still want to go deeper than that. Bravo. Here we go. The 'from' method is written by Ricmoo/team

 static from(value: any): BigNumber {
        if (value instanceof BigNumber) { return value; }

        if (typeof(value) === "string") {
            if (value.match(/^-?0x[0-9a-f]+$/i)) {
                return new BigNumber(_constructorGuard, toHex(value));
            }

            if (value.match(/^-?[0-9]+$/)) {
                return new BigNumber(_constructorGuard, toHex(new BN(value)));
            }

            return logger.throwArgumentError("invalid BigNumber string", "value", value);
        }
Enter fullscreen mode Exit fullscreen mode

If you are still here, understand going below beyond this will push you outside the dev zone of application layer, you will go into decimal conversion mathematics. Your choose!

like it? Then, don't forget to give heart!

Top comments (0)