@xyo-network/xl1-protocol 1.12.39 → 1.12.41

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.
Files changed (41) hide show
  1. package/dist/neutral/amount/ShiftedBigInt.d.ts +16 -0
  2. package/dist/neutral/amount/ShiftedBigInt.d.ts.map +1 -0
  3. package/dist/neutral/amount/ShiftedBigIntConfig.d.ts +8 -0
  4. package/dist/neutral/amount/ShiftedBigIntConfig.d.ts.map +1 -0
  5. package/dist/neutral/amount/Xl1Amount.d.ts +34 -0
  6. package/dist/neutral/amount/Xl1Amount.d.ts.map +1 -0
  7. package/dist/neutral/amount/index.d.ts +5 -0
  8. package/dist/neutral/amount/index.d.ts.map +1 -0
  9. package/dist/neutral/amount/specs/Xl1Amount.spec.d.ts +2 -0
  10. package/dist/neutral/amount/specs/Xl1Amount.spec.d.ts.map +1 -0
  11. package/dist/neutral/amount/splitOnDecimal.d.ts +2 -0
  12. package/dist/neutral/amount/splitOnDecimal.d.ts.map +1 -0
  13. package/dist/neutral/amount/splitOnDecimalToString.d.ts +2 -0
  14. package/dist/neutral/amount/splitOnDecimalToString.d.ts.map +1 -0
  15. package/dist/neutral/index.d.ts +1 -0
  16. package/dist/neutral/index.d.ts.map +1 -1
  17. package/dist/neutral/index.mjs +281 -122
  18. package/dist/neutral/index.mjs.map +1 -1
  19. package/dist/neutral/interfaces/view/AccountBalance.d.ts +1 -1
  20. package/dist/neutral/interfaces/view/AccountBalance.d.ts.map +1 -1
  21. package/dist/neutral/interfaces/view/TransferBalance.d.ts +19 -0
  22. package/dist/neutral/interfaces/view/TransferBalance.d.ts.map +1 -0
  23. package/dist/neutral/interfaces/view/index.d.ts +1 -0
  24. package/dist/neutral/interfaces/view/index.d.ts.map +1 -1
  25. package/dist/neutral/provider/XyoViewer.d.ts +2 -2
  26. package/dist/neutral/provider/XyoViewer.d.ts.map +1 -1
  27. package/dist/neutral/services/StakeIntentService/ChainIndexingServiceStateSchema.d.ts.map +1 -1
  28. package/package.json +19 -18
  29. package/src/amount/ShiftedBigInt.ts +71 -0
  30. package/src/amount/ShiftedBigIntConfig.ts +7 -0
  31. package/src/amount/Xl1Amount.ts +113 -0
  32. package/src/amount/index.ts +4 -0
  33. package/src/amount/specs/Xl1Amount.spec.ts +166 -0
  34. package/src/amount/splitOnDecimal.ts +5 -0
  35. package/src/amount/splitOnDecimalToString.ts +37 -0
  36. package/src/index.ts +1 -0
  37. package/src/interfaces/view/AccountBalance.ts +1 -1
  38. package/src/interfaces/view/TransferBalance.ts +22 -0
  39. package/src/interfaces/view/index.ts +1 -0
  40. package/src/provider/XyoViewer.ts +2 -2
  41. package/src/services/StakeIntentService/ChainIndexingServiceStateSchema.ts +3 -3
@@ -0,0 +1,113 @@
1
+ /* eslint-disable @typescript-eslint/member-ordering */
2
+ import { assertEx } from '@xylabs/assert'
3
+
4
+ import {
5
+ AttoXL1, FemtoXL1, MicroXL1, MilliXL1, NanoXL1, PicoXL1, XL1Places,
6
+ } from '../xl1/index.ts'
7
+ import { ShiftedBigInt } from './ShiftedBigInt.ts'
8
+ import type { ShiftedBigIntConfig } from './ShiftedBigIntConfig.ts'
9
+
10
+ const MAX_XL1_AMOUNT = 2n ** 256n - 1n
11
+ const allowedPlaces = Object.values(XL1Places)
12
+
13
+ export interface XL1AmountInstance {
14
+ value: AttoXL1
15
+
16
+ to(places: bigint | number): bigint
17
+
18
+ milli: MilliXL1
19
+
20
+ micro: MicroXL1
21
+
22
+ nano: NanoXL1
23
+
24
+ pico: PicoXL1
25
+
26
+ femto: FemtoXL1
27
+
28
+ atto: AttoXL1
29
+
30
+ toString(places: number, config: Partial<ShiftedBigIntConfig>): string
31
+ }
32
+
33
+ export class XL1Amount implements XL1AmountInstance {
34
+ value: AttoXL1
35
+ private locale: Intl.LocalesArgument
36
+
37
+ constructor(value: bigint, locale: Intl.LocalesArgument = 'en-US') {
38
+ this.locale = locale
39
+ this.value = AttoXL1(value > MAX_XL1_AMOUNT ? MAX_XL1_AMOUNT : value < 0n ? 0n : value)
40
+ }
41
+
42
+ static from(value: bigint, places: bigint = XL1Places.atto) {
43
+ assertEx(allowedPlaces.includes(places), () => `Invalid conversion places (${places} not in ${allowedPlaces})`)
44
+ return new XL1Amount(value * 10n ** BigInt(places))
45
+ }
46
+
47
+ static fromMilli(value: MilliXL1) {
48
+ return this.from(value, XL1Places.milli)
49
+ }
50
+
51
+ static fromMicro(value: MicroXL1) {
52
+ return this.from(value, XL1Places.micro)
53
+ }
54
+
55
+ static fromNano(value: NanoXL1) {
56
+ return this.from(value, XL1Places.nano)
57
+ }
58
+
59
+ static fromPico(value: PicoXL1) {
60
+ return this.from(value, XL1Places.pico)
61
+ }
62
+
63
+ static fromFemto(value: FemtoXL1) {
64
+ return this.from(value, XL1Places.femto)
65
+ }
66
+
67
+ static fromAtto(value: AttoXL1) {
68
+ return this.from(value, XL1Places.atto)
69
+ }
70
+
71
+ to(places: number | bigint = XL1Places.atto) {
72
+ return this.value / 10n ** BigInt(places)
73
+ }
74
+
75
+ get milli() {
76
+ return MilliXL1(this.to(XL1Places.milli))
77
+ }
78
+
79
+ get micro() {
80
+ return MicroXL1(this.to(XL1Places.micro))
81
+ }
82
+
83
+ get nano() {
84
+ return NanoXL1(this.to(XL1Places.nano))
85
+ }
86
+
87
+ get pico() {
88
+ return PicoXL1(this.to(XL1Places.pico))
89
+ }
90
+
91
+ get femto() {
92
+ return FemtoXL1(this.to(XL1Places.femto))
93
+ }
94
+
95
+ get atto() {
96
+ return AttoXL1(this.to(XL1Places.atto))
97
+ }
98
+
99
+ toString(places: number = Number(XL1Places.atto), config: Partial<ShiftedBigIntConfig> = {}): string {
100
+ assertEx(allowedPlaces.includes(BigInt(places)), () => `Invalid conversion places (${places} not in ${allowedPlaces})`)
101
+ return new ShiftedBigInt(
102
+ this.value,
103
+ {
104
+ places,
105
+ locale: this.locale,
106
+ maxDecimal: places,
107
+ minDecimals: 0,
108
+ maxCharacters: places,
109
+ ...config,
110
+ },
111
+ ).toShortString()
112
+ }
113
+ }
@@ -0,0 +1,4 @@
1
+ export * from './ShiftedBigInt.ts'
2
+ export * from './splitOnDecimal.ts'
3
+ export * from './splitOnDecimalToString.ts'
4
+ export * from './Xl1Amount.ts'
@@ -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,5 @@
1
+ export const splitOnDecimal = (value: bigint, places = 18): [bigint, bigint] => {
2
+ const whole = value / BigInt(10 ** places)
3
+ const decimal = value % BigInt(10 ** places)
4
+ return [whole, decimal]
5
+ }
@@ -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
@@ -1,3 +1,4 @@
1
+ export * from './amount/index.ts'
1
2
  export * from './block/index.ts'
2
3
  export * from './chain/index.ts'
3
4
  export * from './constants/index.ts'
@@ -7,8 +7,8 @@ import type { AttoXL1 } from '../../xl1/index.ts'
7
7
  export type AccountBalanceHistoryItem = [Hash /* block */, Hash /* transaction */, Transfer /* the actual transfer payload */]
8
8
 
9
9
  export interface AccountBalancesViewInterface {
10
- accountBalanceHistories(address: Address): Promisable<Partial<Record<Address, AccountBalanceHistoryItem[]>>>
11
10
  accountBalances(address: Address[]): Promisable<Partial<Record<Address, AttoXL1>>>
11
+ accountBalancesHistories(address: Address[]): Promisable<Partial<Record<Address, AccountBalanceHistoryItem[]>>>
12
12
  }
13
13
 
14
14
  export interface AccountBalanceViewInterface {
@@ -0,0 +1,22 @@
1
+ import type { Address, Hash } from '@xylabs/hex'
2
+ import type { Promisable } from '@xylabs/promise'
3
+
4
+ import type { Transfer } from '../../payload/index.ts'
5
+ import type { AttoXL1 } from '../../xl1/index.ts'
6
+
7
+ export type TransferBalanceHistoryItem = [Hash /* block */, Hash /* transaction */, Transfer /* the actual transfer payload */]
8
+ export type TransferPair = [/* from */ Address, /* to */Address]
9
+
10
+ export interface TransferBalancesViewInterface {
11
+ transferBalances(address: Address[]): Promisable<Partial<Record<Address, Record<Address, AttoXL1>>>>
12
+ transferBalancesHistories(address: Address[]): Promisable<Partial<Record<Address, Record<Address, TransferBalanceHistoryItem[]>>>>
13
+ transferPairBalances(address: TransferPair[]): Promisable<Partial<Record<Address, Record<Address, AttoXL1>>>>
14
+ transferPairBalancesHistories(address: TransferPair[]): Promisable<Partial<Record<Address, Record<Address, TransferBalanceHistoryItem[]>>>>
15
+ }
16
+
17
+ export interface TransferBalanceViewInterface {
18
+ transferBalance(address: Address): Promisable<AttoXL1>
19
+ transferBalanceHistory(address: Address): Promisable<TransferBalanceHistoryItem[]>
20
+ transferPairBalance(address: TransferPair): Promisable<AttoXL1>
21
+ transferPairBalanceHistory(address: TransferPair): Promisable<TransferBalanceHistoryItem[]>
22
+ }
@@ -10,3 +10,4 @@ export * from './StakeTotals.ts'
10
10
  export * from './StepStake.ts'
11
11
  export * from './TimeSync.ts'
12
12
  export * from './Transaction.ts'
13
+ export * from './TransferBalance.ts'
@@ -2,8 +2,8 @@ import type {
2
2
  AccountBalanceViewInterface, BlockViewInterface, ChainViewInterface,
3
3
  ForkViewInterface,
4
4
  StakeViewInterface,
5
- TransactionViewInterface,
5
+ TransactionViewInterface, TransferBalanceViewInterface,
6
6
  } from '../interfaces/index.ts'
7
7
 
8
- export interface XyoViewer extends AccountBalanceViewInterface,
8
+ export interface XyoViewer extends AccountBalanceViewInterface, TransferBalanceViewInterface,
9
9
  ChainViewInterface, BlockViewInterface, TransactionViewInterface, StakeViewInterface, ForkViewInterface {}
@@ -36,8 +36,8 @@ export const isChainIndexingServiceState = <T extends JsonValue = JsonValue>(pay
36
36
  export const asChainIndexingServiceState = AsObjectFactory.create<ChainIndexingServiceState<JsonValue>>(isChainIndexingServiceState)
37
37
 
38
38
  export const isChainIndexingServiceStateWithStorageMeta
39
- = <T extends JsonValue = JsonValue>(value: unknown): value is WithStorageMeta<ChainIndexingServiceState<T>> =>
40
- isChainIndexingServiceState<T>(value) && isStorageMeta(value)
39
+ = <T extends JsonValue = JsonValue>(value: unknown): value is WithStorageMeta<ChainIndexingServiceState<T>> =>
40
+ isChainIndexingServiceState<T>(value) && isStorageMeta(value)
41
41
 
42
42
  export const asChainIndexingServiceStateWithStorageMeta
43
- = AsObjectFactory.create<WithStorageMeta<ChainIndexingServiceState>>(isChainIndexingServiceStateWithStorageMeta)
43
+ = AsObjectFactory.create<WithStorageMeta<ChainIndexingServiceState>>(isChainIndexingServiceStateWithStorageMeta)