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

feat(big number): own implementation of BigNumber #535

Merged
merged 8 commits into from
Jul 27, 2022
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: 1 addition & 1 deletion .changeset/brave-bats-destroy.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@moralisweb3/api': major
'@moralisweb3/api-utils': major
'@moralisweb3/evm-api': major
'@moralisweb3/integration': major
---
Expand Down
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[
"@moralisweb3/core",
"@moralisweb3/auth",
"@moralisweb3/api",
"@moralisweb3/api-utils",
"@moralisweb3/evm-utils",
"@moralisweb3/evm-api",
"@moralisweb3/sol-api"
Expand Down
2 changes: 1 addition & 1 deletion .changeset/itchy-nails-push.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
'test-node': patch
'@moralisweb3/api': patch
'@moralisweb3/api-utils': patch
'@moralisweb3/auth': patch
'@moralisweb3/core': patch
'@moralisweb3/evm-api': patch
Expand Down
2 changes: 1 addition & 1 deletion .changeset/old-cherries-jam.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@moralisweb3/api': major
'@moralisweb3/api-utils': major
---

Generic logic for api modules
13 changes: 13 additions & 0 deletions .changeset/young-buses-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@moralisweb3/core': patch
'@moralisweb3/evm-api': patch
'@moralisweb3/evm-utils': patch
'@moralisweb3/sol-api': patch
'@moralisweb3/sol-utils': patch
'@moralisweb3/api-utils': patch
'@moralisweb3/auth': patch
'@moralisweb3/integration': patch
'moralis': patch
---

Replaced BigNumber from @ethersproject by own implementation.
2 changes: 0 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
"typescript": "^4.5.5"
},
"dependencies": {
"@ethersproject/bignumber": "^5.6.0",
"@ethersproject/units": "^5.6.0",
"@xstate/fsm": "^2.0.0",
"eventemitter3": "^4.0.7",
"axios": "^0.27.2"
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/Config/MoralisConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ interface EvmUtilsConfigValues {
formatEvmChainId: EvmChainIdFormat;
}

// @moralisweb3/api
interface ApiConfigValues {
// @moralisweb3/api-utils
interface ApiUtilsConfigValues {
apiKey: string;
}

Expand All @@ -30,7 +30,7 @@ interface SolApiConfigValues {
export type MoralisConfigValues =
| CoreConfigValues
| EvmUtilsConfigValues
| ApiConfigValues
| ApiUtilsConfigValues
| EvmApiConfigValues
| SolApiConfigValues
| { [key: string]: string | number }; // Other, not strong typed values.
2 changes: 2 additions & 0 deletions packages/core/src/Error/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export enum CoreErrorCode {
CONFIG_INVALID_VALUE = 'C0014',
CONFIG_KEY_ALREADY_EXIST = 'C0015',

BIG_NUMBER_ERROR = 'C0500',

NOT_IMPLEMENTED = 'C9000',
}

Expand Down
57 changes: 57 additions & 0 deletions packages/core/src/dataTypes/BigNumber/BigNumber.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { BigNumber } from './BigNumber';

describe('BigNumber', () => {
it('create() returns integer', () => {
const value = BigNumber.create('1000000000000000000');
expect(value.toString()).toEqual('1000000000000000000');
expect(value.toJSON()).toEqual('0x0de0b6b3a7640000');
expect(value.toDecimal(0)).toEqual('1000000000000000000');
expect(value.toDecimal(18)).toEqual('1.0');
expect(value.toDecimal(5)).toEqual('10000000000000.0');
expect(value.toBigInt()).toEqual(BigInt('1000000000000000000'));
});

it('fromDecimal() returns integer', () => {
const value = BigNumber.fromDecimal('1.1', 18);
expect(value.toString()).toEqual('1100000000000000000');
expect(value.toJSON()).toEqual('0x0f43fc2c04ee0000');
expect(value.toDecimal(0)).toEqual('1100000000000000000');
expect(value.toDecimal(18)).toEqual('1.1');
expect(value.toDecimal(5)).toEqual('11000000000000.0');
});

it('fromDecimal() assumes decimals=0 if not provided', () => {
const value = BigNumber.fromDecimal(100);
expect(value.toString()).toEqual('100');
});

it('does not create a new instance if BigNumber instance passed', () => {
const value = BigNumber.create(0x1);
expect(BigNumber.create(value) === value).toBe(true);
});

describe('math', () => {
const from = (value: number) => BigNumber.create(value);

it('add() multiplies correctly', () => {
expect(from(2).add(from(3)).toString()).toEqual('5');
});

it('sub() subtracts correctly', () => {
expect(from(10).sub(from(4)).toString()).toEqual('6');
});

it('mul() multiplies correctly', () => {
expect(from(2).mul(from(3)).toString()).toEqual('6');
});

it('div() divides correctly', () => {
expect(from(15).div(from(5)).toString()).toEqual('3');
});

it('equals() returns correct value', () => {
expect(from(1).equals(from(1))).toBe(true);
expect(from(1).equals(from(-1))).toBe(false);
});
});
});
63 changes: 63 additions & 0 deletions packages/core/src/dataTypes/BigNumber/BigNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { BigNumberFormatter } from './BigNumberFormatter';
import { BigNumberParser, BigNumberPrimitive } from './BigNumberParser';

export type BigNumberish = BigNumber | BigNumberPrimitive;

export class BigNumber {
public static create(value: BigNumberish): BigNumber {
if (value instanceof BigNumber) {
return value;
}
return new BigNumber(BigNumberParser.parseInt(value));
}

public static fromDecimal(value: BigNumberPrimitive, decimals = 0): BigNumber {
return new BigNumber(BigNumberParser.parseDecimal(value, decimals));
}

private constructor(private readonly value: bigint) {}

public toBigInt(): bigint {
return this.value;
}

public add(value: BigNumberish): BigNumber {
return new BigNumber(this.value + asBigInt(value));
}

public sub(value: BigNumberish): BigNumber {
return new BigNumber(this.value - asBigInt(value));
}

public mul(value: BigNumberish): BigNumber {
return new BigNumber(this.value * asBigInt(value));
}

public div(value: BigNumberish): BigNumber {
return new BigNumber(this.value / asBigInt(value));
}

public equals(value: BigNumber): boolean {
return this.value === value.toBigInt();
}

public toDecimal(decimals: number): string {
return BigNumberFormatter.toDecimal(this.value, decimals);
}

public toString(): string {
return this.value.toString();
}

public toHex(): string {
return BigNumberFormatter.toHex(this.value);
}

public toJSON(): string {
return this.toHex();
}
}

function asBigInt(value: BigNumberish): bigint {
return BigNumber.create(value).toBigInt();
}
46 changes: 46 additions & 0 deletions packages/core/src/dataTypes/BigNumber/BigNumberFormatter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { BigNumberFormatter } from './BigNumberFormatter';

describe('BigNumberFormatter', () => {
describe('toDecimal()', () => {
const toDecimal = (value: string, decimals: number) => BigNumberFormatter.toDecimal(BigInt(value), decimals);

it('returns correct value', () => {
expect(toDecimal('0', 0)).toEqual('0');
expect(toDecimal('-0', 0)).toEqual('0');
expect(toDecimal('0', 2)).toEqual('0.0');
expect(toDecimal('-0', 2)).toEqual('0.0');
expect(toDecimal('0', 10)).toEqual('0.0');
expect(toDecimal('-256', 0)).toEqual('-256');
expect(toDecimal('256', 0)).toEqual('256');
expect(toDecimal('256', 1)).toEqual('25.6');
expect(toDecimal('-256', 1)).toEqual('-25.6');
expect(toDecimal('256', 2)).toEqual('2.56');
expect(toDecimal('256', 5)).toEqual('0.00256');
expect(toDecimal('256', 10)).toEqual('0.0000000256');
expect(toDecimal('-256', 10)).toEqual('-0.0000000256');
expect(toDecimal('256', 18)).toEqual('0.000000000000000256');
expect(toDecimal('-256', 18)).toEqual('-0.000000000000000256');
});

it('throws an error if decimals is negative', () => {
expect(() => toDecimal('0', -1)).toThrowError('Invalid decimals');
});
});

describe('toHex()', () => {
const toHex = (value: string) => BigNumberFormatter.toHex(BigInt(value));

it('returns correct value', () => {
expect(toHex('10')).toEqual('0x0a');
expect(toHex('200')).toEqual('0xc8');
expect(toHex('0')).toEqual('0x00');
expect(toHex('351')).toEqual('0x015f');
expect(toHex('123456789')).toEqual('0x075bcd15');
expect(toHex('-0')).toEqual('0x00');
expect(toHex('0')).toEqual('0x00');
expect(toHex('-100')).toEqual('-0x64');
expect(toHex('-1')).toEqual('-0x01');
expect(toHex('-123456789')).toEqual('-0x075bcd15');
});
});
});
53 changes: 53 additions & 0 deletions packages/core/src/dataTypes/BigNumber/BigNumberFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { CoreErrorCode, MoralisCoreError } from '../../Error';

export class BigNumberFormatter {
public static toDecimal(value: bigint, decimals: number): string {
if (decimals < 0) {
throw new MoralisCoreError({
code: CoreErrorCode.BIG_NUMBER_ERROR,
message: 'Invalid decimals',
});
}

let result = value.toString();
if (decimals === 0) {
return result;
}

const isNegative = result.startsWith('-');
if (isNegative) {
result = result.substring(1);
}
result = result.padStart(decimals, '0');

const dot = result.length - decimals;
const whole = dot === 0 ? '0' : result.substring(0, dot);
const fraction = result.substring(dot);

result = whole + '.' + fraction;

while (result[result.length - 1] === '0' && result[result.length - 2] !== '.') {
result = result.substring(0, result.length - 1);
}
if (isNegative) {
result = '-' + result;
}
return result;
}

public static toHex(value: bigint): string {
let result = value.toString(16);
const isNegative = result.startsWith('-');
if (isNegative) {
result = result.substring(1);
}
if (result.length % 2 !== 0) {
result = '0' + result;
}
result = '0x' + result;
if (isNegative) {
result = '-' + result;
}
return result;
}
}
Loading