/
number128.ts
132 lines (102 loc) · 3.6 KB
/
number128.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { BN } from "@project-serum/anchor"
import { bnToNumber, TokenAmount } from "../token"
export class Number128 {
static readonly PRECISION = 10
static readonly ONE = new Number128(new BN(10_000_000_000))
static readonly ZERO = new Number128(new BN(0))
static readonly MAX = new Number128(new BN("340282366920938463463374607431768211455"))
private static readonly U64_MAX = new BN("18446744073709551615")
private static readonly BPS_EXPONENT = -4
constructor(private _bn: BN) {}
toNumber() {
return bnToNumber(this._bn) / 10 ** Number128.PRECISION
}
toTokenAmount(decimals: number) {
return TokenAmount.lamports(this.toBn(0), decimals)
}
/** Removes the fractional component from the number.*/
toBn(exponent: number): BN {
let extraPrecision = Number128.PRECISION + exponent
let precValue = Number128.tenPow(new BN(Math.abs(extraPrecision)))
if (extraPrecision < 0) {
return this._bn.mul(precValue)
} else {
return this._bn.div(precValue)
}
}
/** Removes the fractional component from the number. Throws if the number is not within the range of a u64. */
toU64(exponent: number): BN {
const targetValue = this.toBn(exponent)
if (targetValue.gt(Number128.U64_MAX)) {
throw new Error("cannot convert to u64 due to overflow")
}
if (targetValue.lt(Number128.ZERO._bn)) {
throw new Error("cannot convert to u64 because value < 0")
}
return targetValue
}
static fromDecimal(value: BN, exponent: number): Number128 {
let extraPrecision = Number128.PRECISION + exponent
let precValue = Number128.tenPow(new BN(Math.abs(extraPrecision)))
if (extraPrecision < 0) {
return new Number128(value.div(precValue))
} else {
return new Number128(value.mul(precValue))
}
}
/** Convert from basis points */
static fromBps(basisPoints: BN): Number128 {
return this.fromDecimal(basisPoints, this.BPS_EXPONENT)
}
static fromBits(bits: number[]): Number128 {
return new Number128(new BN(bits, "le"))
}
static from(bn: BN): Number128 {
return new Number128(bn.mul(Number128.ONE._bn))
}
saturatingAdd(rhs: Number128): Number128 {
return new Number128(Number128.clamp(this._bn.add(rhs._bn), Number128.ZERO._bn, Number128.MAX._bn))
}
saturatingSub(rhs: Number128): Number128 {
return new Number128(Number128.clamp(this._bn.sub(rhs._bn), Number128.ZERO._bn, Number128.MAX._bn))
}
saturatingMul(rhs: Number128): Number128 {
return new Number128(Number128.clamp(this._bn.mul(rhs._bn), Number128.ZERO._bn, Number128.MAX._bn))
}
private static clamp(value: BN, low: BN, high: BN): BN {
return BN.max(BN.min(value, high), low)
}
add(rhs: Number128): Number128 {
return new Number128(this._bn.add(rhs._bn))
}
sub(rhs: Number128): Number128 {
return new Number128(this._bn.sub(rhs._bn))
}
mul(rhs: Number128): Number128 {
return new Number128(this._bn.mul(rhs._bn).div(Number128.ONE._bn))
}
div(rhs: Number128): Number128 {
return new Number128(this._bn.mul(Number128.ONE._bn).div(rhs._bn))
}
public lt(b: Number128): boolean {
return this._bn.lt(b._bn)
}
public gt(b: Number128): boolean {
return this._bn.gt(b._bn)
}
public eq(b: Number128): boolean {
return this._bn.eq(b._bn)
}
public isZero(): boolean {
return this._bn.isZero()
}
static min(a: Number128, b: Number128): Number128 {
return new Number128(BN.min(a._bn, b._bn))
}
static max(a: Number128, b: Number128): Number128 {
return new Number128(BN.max(a._bn, b._bn))
}
static tenPow(exponent: BN) {
return new BN(10).pow(exponent)
}
}