@exodus/bigint 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # @exodus/bigint
2
+
3
+ Immutable API wrapper for big integers, using BigInt if available in the JS environment and falling back to bn.js if it isn't
4
+
5
+ ## Usage
6
+
7
+ ```js
8
+ import BigIntWrapper from '@exodus/bigint'
9
+
10
+ const three = BigIntWrapper.wrap(3)
11
+ const five = BigIntWrapper.wrap(5)
12
+
13
+ // perform arithmetic
14
+ // see full API: ./src/native-bigint.js and ./src/__tests__/index.test.js
15
+ three.add(five).mul(three).div(five).pow(three).sub(five)
16
+ ```
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@exodus/bigint",
3
+ "version": "1.0.0",
4
+ "description": "bigint wrappers for native BigUnt, bn.js and others",
5
+ "main": "src/index.js",
6
+ "author": "Exodus Movement, Inc.",
7
+ "license": "UNLICENSED",
8
+ "homepage": "https://github.com/ExodusMovement/exodus-core/tree/master/packages/bigint#readme",
9
+ "files": [
10
+ "src",
11
+ "!**/__tests__",
12
+ "README.md"
13
+ ],
14
+ "dependencies": {
15
+ "bn.js": "^4.11.0",
16
+ "lodash": "^4.17.11",
17
+ "minimalistic-assert": "^1.0.1"
18
+ },
19
+ "devDependencies": {
20
+ "eslint": "^8.43.0",
21
+ "jest": "^29.3.1"
22
+ },
23
+ "scripts": {
24
+ "test": "jest",
25
+ "lint": "eslint . --ignore-path ../../.gitignore",
26
+ "lint:fix": "yarn lint --fix"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/ExodusMovement/exodus-core.git"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/ExodusMovement/exodus-core/issues?q=is%3Aissue+is%3Aopen+label%3Abigint"
34
+ }
35
+ }
package/src/bn.js ADDED
@@ -0,0 +1,160 @@
1
+ import assert from 'minimalistic-assert'
2
+ import BN from 'bn.js'
3
+
4
+ // TODO: calculator delegator should wrap/unwrap all BN instances
5
+ // so that callers never gett a raw BN instance back
6
+
7
+ const FACTORY_SYMBOL = Symbol('bn-wrapper')
8
+
9
+ const unwrap = (value) => (value instanceof Wrapper ? value.unwrap() : value)
10
+
11
+ export default class Wrapper {
12
+ static name = 'bn.js'
13
+ static isUnderlyingInstance(a) {
14
+ return BN.isBN(a)
15
+ }
16
+ static wrap(value, base) {
17
+ return new Wrapper(FACTORY_SYMBOL, value, base)
18
+ }
19
+ static get ZERO() {
20
+ return ZERO
21
+ }
22
+ static get TEN() {
23
+ return TEN
24
+ }
25
+
26
+ __value__
27
+
28
+ constructor(factorySymbol, value, base = 10) {
29
+ if (factorySymbol !== FACTORY_SYMBOL) throw new Error('use wrap() instead')
30
+
31
+ if (BN.isBN(value)) {
32
+ this.__value__ = value
33
+ } else {
34
+ if (typeof value !== 'string' && typeof value !== 'number') {
35
+ throw new TypeError(`Unsupported type: ${typeof value}`)
36
+ }
37
+
38
+ if (base !== 10 && base !== 16) throw new TypeError(`Unsupported base: ${base}`)
39
+
40
+ this.__value__ = new BN(value, base)
41
+ }
42
+ }
43
+
44
+ unwrap() {
45
+ return this.__value__
46
+ }
47
+
48
+ add(value) {
49
+ return Wrapper.wrap(this.__value__.add(unwrap(value)))
50
+ }
51
+
52
+ mutateAdd(value) {
53
+ this.__value__.iadd(unwrap(value))
54
+ return this
55
+ }
56
+
57
+ sub(value) {
58
+ return Wrapper.wrap(this.__value__.sub(unwrap(value)))
59
+ }
60
+
61
+ mutateSub(value) {
62
+ this.__value__.isub(unwrap(value))
63
+ return this
64
+ }
65
+
66
+ mul(value) {
67
+ return Wrapper.wrap(this.__value__.mul(unwrap(value)))
68
+ }
69
+
70
+ mutateMul(value) {
71
+ this.__value__.imul(unwrap(value))
72
+ return this
73
+ }
74
+
75
+ div(value) {
76
+ return Wrapper.wrap(this.__value__.div(unwrap(value)))
77
+ }
78
+
79
+ mutateDiv(value) {
80
+ this.__value__.idivn(unwrap(value))
81
+ return this
82
+ }
83
+
84
+ mod(value) {
85
+ return Wrapper.wrap(this.__value__.mod(unwrap(value)))
86
+ }
87
+
88
+ pow(value) {
89
+ return Wrapper.wrap(this.__value__.pow(unwrap(value)))
90
+ }
91
+
92
+ negate() {
93
+ return Wrapper.wrap(this.__value__.neg())
94
+ }
95
+
96
+ mutateNegate() {
97
+ return Wrapper.wrap(this.__value__.ineg())
98
+ }
99
+
100
+ abs() {
101
+ return this.isNegative() ? Wrapper.wrap(this.__value__.abs()) : this
102
+ }
103
+
104
+ mutateAbs() {
105
+ this.__value__.iabs()
106
+ return this
107
+ }
108
+
109
+ gte(value) {
110
+ return this.__value__.gte(unwrap(value))
111
+ }
112
+
113
+ gt(value) {
114
+ return this.__value__.gt(unwrap(value))
115
+ }
116
+
117
+ lte(value) {
118
+ return this.__value__.lte(unwrap(value))
119
+ }
120
+
121
+ lt(value) {
122
+ return this.__value__.lt(unwrap(value))
123
+ }
124
+
125
+ eq(value) {
126
+ return this.__value__.eq(unwrap(value))
127
+ }
128
+
129
+ isZero() {
130
+ return this.__value__.isZero()
131
+ }
132
+
133
+ isNegative() {
134
+ return this.__value__.isNeg()
135
+ }
136
+
137
+ isPositive() {
138
+ return this.__value__.gt(ZERO.unwrap())
139
+ }
140
+
141
+ toNumber() {
142
+ return this.__value__.toNumber()
143
+ }
144
+
145
+ toString(base) {
146
+ assert(typeof base === 'number', 'expected number "base"')
147
+ return this.__value__.toString(base)
148
+ }
149
+
150
+ toBaseBufferLE(length) {
151
+ return this.__value__.toBuffer('le', length)
152
+ }
153
+
154
+ toBaseBufferBE(length) {
155
+ return this.__value__.toBuffer('be', length)
156
+ }
157
+ }
158
+
159
+ const ZERO = new Wrapper(FACTORY_SYMBOL, 0)
160
+ const TEN = new Wrapper(FACTORY_SYMBOL, 10)
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ const Wrapper =
2
+ typeof BigInt === 'function' ? require('./native-bigint').default : require('./bn').default
3
+
4
+ export default Wrapper
@@ -0,0 +1,173 @@
1
+ // TODO: calculator delegator should wrap/unwrap all BigInt instances
2
+ // so that callers never gett a raw BigInt instance back
3
+
4
+ import { memoize } from 'lodash'
5
+ import assert from 'minimalistic-assert'
6
+
7
+ const FACTORY_SYMBOL = Symbol('bigint-wrapper')
8
+
9
+ const unwrap = (value) => (value instanceof Wrapper ? value.unwrap() : value)
10
+
11
+ const memoizedWrap = memoize((value) => Wrapper.wrap(value))
12
+
13
+ export default class Wrapper {
14
+ static name = 'native-bigint'
15
+
16
+ static wrap(numberLike, base = 10) {
17
+ if (typeof numberLike === 'bigint') return new Wrapper(FACTORY_SYMBOL, numberLike)
18
+ if (typeof numberLike === 'number') return new Wrapper(FACTORY_SYMBOL, BigInt(numberLike))
19
+ if (typeof numberLike !== 'string') throw new Error(`Unsupported type: ${typeof numberLike}`)
20
+
21
+ switch (base) {
22
+ case 10:
23
+ return new Wrapper(FACTORY_SYMBOL, BigInt(numberLike))
24
+ case 16:
25
+ return new Wrapper(FACTORY_SYMBOL, BigInt('0x' + numberLike))
26
+ default:
27
+ throw new Error(`Unsupported base: ${base}`)
28
+ }
29
+ }
30
+
31
+ static isUnderlyingInstance(value) {
32
+ return typeof value === 'bigint'
33
+ }
34
+
35
+ static get ZERO() {
36
+ return memoizedWrap(0n)
37
+ }
38
+
39
+ static get TEN() {
40
+ return memoizedWrap(10n)
41
+ }
42
+
43
+ __value__
44
+
45
+ constructor(factorySymbol, value) {
46
+ if (factorySymbol !== FACTORY_SYMBOL) throw new Error('use wrap() instead')
47
+ this.__value__ = value
48
+ }
49
+
50
+ unwrap() {
51
+ return this.__value__
52
+ }
53
+
54
+ add(value) {
55
+ return Wrapper.wrap(this.__value__ + unwrap(value))
56
+ }
57
+
58
+ mutateAdd(value) {
59
+ this.__value__ += unwrap(value)
60
+ return this
61
+ }
62
+
63
+ sub(value) {
64
+ return Wrapper.wrap(this.__value__ - unwrap(value))
65
+ }
66
+
67
+ mutateSub(value) {
68
+ this.__value__ -= unwrap(value)
69
+ return this
70
+ }
71
+
72
+ mul(value) {
73
+ return Wrapper.wrap(this.__value__ * unwrap(value))
74
+ }
75
+
76
+ mutateMul(value) {
77
+ this.__value__ *= unwrap(value)
78
+ return this
79
+ }
80
+
81
+ div(value) {
82
+ return Wrapper.wrap(this.__value__ / unwrap(value))
83
+ }
84
+
85
+ mutateDiv(value) {
86
+ this.__value__ /= unwrap(value)
87
+ return this
88
+ }
89
+
90
+ mod(value) {
91
+ return Wrapper.wrap(this.__value__ % unwrap(value))
92
+ }
93
+
94
+ pow(value) {
95
+ return Wrapper.wrap(this.__value__ ** unwrap(value))
96
+ }
97
+
98
+ negate() {
99
+ return Wrapper.wrap(-this.__value__)
100
+ }
101
+
102
+ mutateNegate() {
103
+ this.__value__ = -this.__value__
104
+ return this
105
+ }
106
+
107
+ abs() {
108
+ return this.__value__ < 0n ? Wrapper.wrap(-this.__value__) : this
109
+ }
110
+
111
+ mutateAbs() {
112
+ if (this.__value__ < 0n) this.__value__ = -this.__value__
113
+ return this
114
+ }
115
+
116
+ gte(value) {
117
+ return this.__value__ >= unwrap(value)
118
+ }
119
+
120
+ gt(value) {
121
+ return this.__value__ > unwrap(value)
122
+ }
123
+
124
+ lte(value) {
125
+ return this.__value__ <= unwrap(value)
126
+ }
127
+
128
+ lt(value) {
129
+ return this.__value__ < unwrap(value)
130
+ }
131
+
132
+ eq(value) {
133
+ return this.__value__ === unwrap(value)
134
+ }
135
+
136
+ isZero() {
137
+ return this.__value__ === 0n
138
+ }
139
+
140
+ isNegative() {
141
+ return this.__value__ < 0n
142
+ }
143
+
144
+ isPositive() {
145
+ return this.__value__ > 0n
146
+ }
147
+
148
+ toString(base) {
149
+ assert(typeof base === 'number', 'expected number "base"')
150
+ return this.__value__.toString(base)
151
+ }
152
+
153
+ toNumber() {
154
+ return Number(this.__value__)
155
+ }
156
+
157
+ #getWidth() {
158
+ const bitLength = this.__value__.toString(2).length
159
+ return Math.ceil(bitLength / 8)
160
+ }
161
+
162
+ toBaseBufferLE(width = this.#getWidth()) {
163
+ const hex = this.__value__.toString(16)
164
+ const buffer = Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex')
165
+ buffer.reverse()
166
+ return buffer
167
+ }
168
+
169
+ toBaseBufferBE(width = this.#getWidth()) {
170
+ const hex = this.__value__.toString(16)
171
+ return Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex')
172
+ }
173
+ }