From 99cd75a23bd56a9112d9cb1cde8a2d6a3fb8f998 Mon Sep 17 00:00:00 2001 From: trungbach Date: Thu, 26 Oct 2023 18:53:38 +0700 Subject: [PATCH] feat: add bigdecimal class --- .github/workflows/publish_decimal.yml | 49 +++++++++++ packages/bigdecimal/package.json | 10 +++ packages/bigdecimal/src/bigdecimal.ts | 106 ++++++++++++++++++++++++ packages/bigdecimal/src/index.ts | 1 + packages/bigdecimal/tests/tsconfig.json | 8 ++ packages/bigdecimal/tsconfig.json | 15 ++++ 6 files changed, 189 insertions(+) create mode 100644 .github/workflows/publish_decimal.yml create mode 100644 packages/bigdecimal/package.json create mode 100644 packages/bigdecimal/src/bigdecimal.ts create mode 100644 packages/bigdecimal/src/index.ts create mode 100644 packages/bigdecimal/tests/tsconfig.json create mode 100644 packages/bigdecimal/tsconfig.json diff --git a/.github/workflows/publish_decimal.yml b/.github/workflows/publish_decimal.yml new file mode 100644 index 000000000..d8ff37862 --- /dev/null +++ b/.github/workflows/publish_decimal.yml @@ -0,0 +1,49 @@ +name: publish_bigdecimal + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [feat/bigdecimal] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + runs-on: ubuntu-20.04 + strategy: + matrix: + node-version: ["18"] + + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.8.0 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + id: yarn-cache + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + ./node_modules/ + key: ${{ runner.os }}-yarn-${{ hashFiles('./yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Build + run: yarn build packages/bigdecimal + - name: Authenticate with private NPM package + run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc + - name: Publish + id: publish + continue-on-error: false + run: yarn deploy packages/bigdecimal + env: + CI: false + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/bigdecimal/package.json b/packages/bigdecimal/package.json new file mode 100644 index 000000000..4e2ab252f --- /dev/null +++ b/packages/bigdecimal/package.json @@ -0,0 +1,10 @@ +{ + "name": "@oraichain/bigdecimal", + "version": "1.0.0", + "main": "build/index.js", + "files": [ + "build/" + ], + "license": "MIT", + "devDependencies": {} +} diff --git a/packages/bigdecimal/src/bigdecimal.ts b/packages/bigdecimal/src/bigdecimal.ts new file mode 100644 index 000000000..246902183 --- /dev/null +++ b/packages/bigdecimal/src/bigdecimal.ts @@ -0,0 +1,106 @@ +type DecimalLike = string | number | bigint | BigDecimal; + +class BigDecimal { + private bigInt: bigint; + + constructor(value: DecimalLike, protected decimals: number = 6) { + if (typeof value === "string") { + const [ints, decis] = value.split("."); + const padding = decis ? decis.padEnd(decimals, "0").substring(0, decimals) : "0".repeat(decimals); + this.bigInt = BigInt(ints + padding); + } else if (typeof value === "number") { + return new BigDecimal(value.toString(), decimals); + } else if (typeof value === "bigint") { + this.bigInt = value; + } else { + this.bigInt = value.bigInt; + this.decimals = value.decimals; + } + } + + private processDecimal = (value: DecimalLike): BigDecimal => { + if (value instanceof BigDecimal) { + if (value.decimals > this.decimals) { + this.bigInt *= 10n ** BigInt(value.decimals - this.decimals); + this.decimals = value.decimals; + } else if (this.decimals > value.decimals) { + value.bigInt *= 10n ** BigInt(this.decimals - value.decimals); + value.decimals = this.decimals; + } + // same decimal + return value; + } + return new BigDecimal(value, this.decimals); + }; + + toString() { + const str = this.bigInt.toString(); + let ret = str.slice(0, -this.decimals); + const denominator = str.slice(-this.decimals).replace(/0+$/, ""); + if (denominator) { + ret += "." + denominator; + } + return ret; + } + + toNumber() { + return Number(this.toString()); + } + + clone(): BigDecimal { + return new BigDecimal(this); + } + + private iadd(other: DecimalLike) { + const otherDecimal = this.processDecimal(other); + this.bigInt += otherDecimal.bigInt; + return this; + } + + private isub(other: BigDecimal) { + const otherDecimal = this.processDecimal(other); + this.bigInt -= otherDecimal.bigInt; + return this; + } + + private idiv(other: BigDecimal) { + const otherDecimal = this.processDecimal(other); + this.bigInt = (this.bigInt * 10n ** BigInt(this.decimals)) / otherDecimal.bigInt; + return this; + } + + private imul(other: BigDecimal) { + const otherDecimal = this.processDecimal(other); + this.bigInt *= otherDecimal.bigInt; + this.bigInt /= 10n ** BigInt(this.decimals); + return this; + } + + add(other: BigDecimal) { + return this.clone().iadd(other); + } + + sub(other: BigDecimal) { + return this.clone().isub(other); + } + + div(other: BigDecimal) { + return this.clone().idiv(other); + } + + mul(other: BigDecimal) { + return this.clone().imul(other); + } + + valueOf() { + return this.toNumber(); + } +} + +/** + * Example + * const a = new BigDecimal("12345.6789", 10); + * const b = new BigDecimal("2345"); + * // @ts-ignore + * console.log(a.sub(b).toNumber(), a.add(b).toNumber(), a.mul(b).toNumber(), a.div(b).mul(2).add(2.78).toNumber()); + */ diff --git a/packages/bigdecimal/src/index.ts b/packages/bigdecimal/src/index.ts new file mode 100644 index 000000000..a6047e4e4 --- /dev/null +++ b/packages/bigdecimal/src/index.ts @@ -0,0 +1 @@ +export * from "./bigdecimal"; diff --git a/packages/bigdecimal/tests/tsconfig.json b/packages/bigdecimal/tests/tsconfig.json new file mode 100644 index 000000000..686c2b27c --- /dev/null +++ b/packages/bigdecimal/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "isolatedModules": false, + "strict": false, + "strictNullChecks": false + }, +} \ No newline at end of file diff --git a/packages/bigdecimal/tsconfig.json b/packages/bigdecimal/tsconfig.json new file mode 100644 index 000000000..fa0134db3 --- /dev/null +++ b/packages/bigdecimal/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declaration": true, + "rootDir": "src" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules/" + ] +} \ No newline at end of file