@xyo-network/xl1-protocol 1.12.39 → 1.12.40
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/dist/neutral/amount/ShiftedBigInt.d.ts +16 -0
- package/dist/neutral/amount/ShiftedBigInt.d.ts.map +1 -0
- package/dist/neutral/amount/ShiftedBigIntConfig.d.ts +8 -0
- package/dist/neutral/amount/ShiftedBigIntConfig.d.ts.map +1 -0
- package/dist/neutral/amount/Xl1Amount.d.ts +34 -0
- package/dist/neutral/amount/Xl1Amount.d.ts.map +1 -0
- package/dist/neutral/amount/index.d.ts +5 -0
- package/dist/neutral/amount/index.d.ts.map +1 -0
- package/dist/neutral/amount/specs/Xl1Amount.spec.d.ts +2 -0
- package/dist/neutral/amount/specs/Xl1Amount.spec.d.ts.map +1 -0
- package/dist/neutral/amount/splitOnDecimal.d.ts +2 -0
- package/dist/neutral/amount/splitOnDecimal.d.ts.map +1 -0
- package/dist/neutral/amount/splitOnDecimalToString.d.ts +2 -0
- package/dist/neutral/amount/splitOnDecimalToString.d.ts.map +1 -0
- package/dist/neutral/index.d.ts +1 -0
- package/dist/neutral/index.d.ts.map +1 -1
- package/dist/neutral/index.mjs +281 -122
- package/dist/neutral/index.mjs.map +1 -1
- package/package.json +3 -2
- package/src/amount/ShiftedBigInt.ts +71 -0
- package/src/amount/ShiftedBigIntConfig.ts +7 -0
- package/src/amount/Xl1Amount.ts +113 -0
- package/src/amount/index.ts +4 -0
- package/src/amount/specs/Xl1Amount.spec.ts +166 -0
- package/src/amount/splitOnDecimal.ts +5 -0
- package/src/amount/splitOnDecimalToString.ts +37 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe, expect, it,
|
|
3
|
+
} from 'vitest'
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AttoXL1, FemtoXL1, MicroXL1, MilliXL1, NanoXL1, PicoXL1,
|
|
7
|
+
} from '../../xl1/index.ts'
|
|
8
|
+
import { XL1Places } from '../../xl1/index.ts'
|
|
9
|
+
import { XL1Amount } from '../Xl1Amount.ts'
|
|
10
|
+
|
|
11
|
+
describe('XL1Amount', () => {
|
|
12
|
+
describe('constructor', () => {
|
|
13
|
+
it('should create an instance with valid value', () => {
|
|
14
|
+
const amount = new XL1Amount(1000n)
|
|
15
|
+
expect(amount.value).toBe(1000n)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should clamp negative values to 0', () => {
|
|
19
|
+
const amount = new XL1Amount(-1000n)
|
|
20
|
+
expect(amount.value).toBe(0n)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// it('should clamp values exceeding MAX_XL1_AMOUNT', () => {
|
|
24
|
+
// const maxValue = 2n ** 256n - 1n
|
|
25
|
+
// const amount = new XL1Amount(maxValue + 1n)
|
|
26
|
+
// expect(amount.value).toBe(maxValue)
|
|
27
|
+
// })
|
|
28
|
+
|
|
29
|
+
it('should accept custom locale', () => {
|
|
30
|
+
const amount = new XL1Amount(1000n, 'de-DE')
|
|
31
|
+
expect(amount.value).toBe(1000n)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('static from method', () => {
|
|
36
|
+
it('should create XL1Amount from value and places', () => {
|
|
37
|
+
const amount = XL1Amount.from(1n, XL1Places.milli)
|
|
38
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.milli)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should default to atto places', () => {
|
|
42
|
+
const amount = XL1Amount.from(1000n)
|
|
43
|
+
expect(amount.value).toBe(1000n)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should throw error for invalid places', () => {
|
|
47
|
+
expect(() => XL1Amount.from(1000n, 99n)).toThrow('Invalid conversion places')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('static factory methods', () => {
|
|
52
|
+
it('should create from milli', () => {
|
|
53
|
+
const amount = XL1Amount.fromMilli(1n as MilliXL1)
|
|
54
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.milli)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should create from micro', () => {
|
|
58
|
+
const amount = XL1Amount.fromMicro(1n as MicroXL1)
|
|
59
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.micro)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should create from nano', () => {
|
|
63
|
+
const amount = XL1Amount.fromNano(1n as NanoXL1)
|
|
64
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.nano)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should create from pico', () => {
|
|
68
|
+
const amount = XL1Amount.fromPico(1n as PicoXL1)
|
|
69
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.pico)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should create from femto', () => {
|
|
73
|
+
const amount = XL1Amount.fromFemto(1n as FemtoXL1)
|
|
74
|
+
expect(amount.value).toBe(1n * 10n ** XL1Places.femto)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should create from atto', () => {
|
|
78
|
+
const amount = XL1Amount.fromAtto(1000n as AttoXL1)
|
|
79
|
+
expect(amount.value).toBe(1000n)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('to method', () => {
|
|
84
|
+
it('should convert to specified places', () => {
|
|
85
|
+
const amount = new XL1Amount(1_000_000_000_000_000_000n) // 1 * 10^18
|
|
86
|
+
expect(amount.to(XL1Places.milli)).toBe(1000n)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should default to atto places', () => {
|
|
90
|
+
const amount = new XL1Amount(1000n)
|
|
91
|
+
expect(amount.to()).toBe(1000n)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should handle bigint places', () => {
|
|
95
|
+
const amount = new XL1Amount(1_000_000_000_000_000_000n)
|
|
96
|
+
expect(amount.to(XL1Places.milli)).toBe(1000n)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('getters', () => {
|
|
101
|
+
const oneXl1 = 1n * 10n ** BigInt(XL1Places.xl1) // 1 * 10^18
|
|
102
|
+
const amount = new XL1Amount(oneXl1) // 1 * 10^18
|
|
103
|
+
|
|
104
|
+
it('should get milli value', () => {
|
|
105
|
+
expect(amount.milli).toBe(1000n)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should get micro value', () => {
|
|
109
|
+
expect(amount.micro).toBe(1_000_000n)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should get nano value', () => {
|
|
113
|
+
expect(amount.nano).toBe(1_000_000_000n)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should get pico value', () => {
|
|
117
|
+
expect(amount.pico).toBe(1_000_000_000_000n)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should get femto value', () => {
|
|
121
|
+
expect(amount.femto).toBe(1_000_000_000_000_000n)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should get atto value', () => {
|
|
125
|
+
expect(amount.atto).toBe(oneXl1)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('toString method', () => {
|
|
130
|
+
it('should convert to string with default places', () => {
|
|
131
|
+
const amount = new XL1Amount(1_000_000_000_000_000_000n)
|
|
132
|
+
const result = amount.toString()
|
|
133
|
+
expect(typeof result).toBe('string')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should convert to string with specified places', () => {
|
|
137
|
+
const amount = new XL1Amount(1_000_000_000_000_000_000n)
|
|
138
|
+
const result = amount.toString(Number(XL1Places.milli))
|
|
139
|
+
expect(typeof result).toBe('string')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should accept custom config', () => {
|
|
143
|
+
const amount = new XL1Amount(1_000_000_000_000_000_000n)
|
|
144
|
+
const result = amount.toString(Number(XL1Places.milli), { minDecimals: 2 })
|
|
145
|
+
expect(typeof result).toBe('string')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should throw error for invalid places', () => {
|
|
149
|
+
const amount = new XL1Amount(1000n)
|
|
150
|
+
expect(() => amount.toString(99)).toThrow('Invalid conversion places')
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('edge cases', () => {
|
|
155
|
+
it('should handle zero value', () => {
|
|
156
|
+
const amount = new XL1Amount(0n)
|
|
157
|
+
expect(amount.value).toBe(0n)
|
|
158
|
+
expect(amount.to(XL1Places.milli)).toBe(0n)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('should handle division with remainder', () => {
|
|
162
|
+
const amount = new XL1Amount(1_500_000_000_000_000_000n)
|
|
163
|
+
expect(amount.to(XL1Places.milli)).toBe(1500n)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { splitOnDecimal } from './splitOnDecimal.ts'
|
|
2
|
+
|
|
3
|
+
export const splitOnDecimalToString = (
|
|
4
|
+
value: bigint,
|
|
5
|
+
places = 18,
|
|
6
|
+
maxDecimal = places,
|
|
7
|
+
maxCharacters = 9,
|
|
8
|
+
minDecimals = 1,
|
|
9
|
+
locale: Intl.LocalesArgument = 'en-US',
|
|
10
|
+
): string => {
|
|
11
|
+
const [whole, decimal] = splitOnDecimal(value, places)
|
|
12
|
+
if (whole === 0n && decimal < 10 ** maxDecimal && decimal !== 0n) return '< 0.'.padEnd(maxDecimal + 5, '0') + '1'
|
|
13
|
+
|
|
14
|
+
const wholeCharacters = whole.toString(10).length
|
|
15
|
+
const calcMaxDecimalCharacters = maxCharacters === -1 ? places : wholeCharacters > maxCharacters ? 0 : maxCharacters - wholeCharacters
|
|
16
|
+
const maxDecimalCharacters = Math.min(maxDecimal, calcMaxDecimalCharacters)
|
|
17
|
+
|
|
18
|
+
// Format whole number with thousand separators according to locale
|
|
19
|
+
const formattedWhole = new Intl.NumberFormat(locale, {
|
|
20
|
+
maximumFractionDigits: 0,
|
|
21
|
+
useGrouping: true,
|
|
22
|
+
}).format(whole)
|
|
23
|
+
|
|
24
|
+
// Get decimal separator for the locale
|
|
25
|
+
const decimalSeparator = new Intl.NumberFormat(locale)
|
|
26
|
+
.formatToParts(1.1)
|
|
27
|
+
.find(part => part.type === 'decimal')?.value ?? '.'
|
|
28
|
+
|
|
29
|
+
// Pad decimal part to correct number of places
|
|
30
|
+
let paddedDecimal = decimal.toString().padStart(places, '0').slice(0, maxDecimalCharacters)
|
|
31
|
+
// remove unneeded trailing zeros (honoring minDecimals)
|
|
32
|
+
while (paddedDecimal.length > minDecimals && paddedDecimal.endsWith('0')) {
|
|
33
|
+
paddedDecimal = paddedDecimal.slice(0, -1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return `${formattedWhole}${paddedDecimal.length > 0 ? decimalSeparator : ''}${paddedDecimal}`
|
|
37
|
+
}
|
package/src/index.ts
CHANGED