sushi 6.6.11 → 6.7.1
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/_cjs/evm/abi/selfPermitAbi/index.js +0 -2
- package/_cjs/evm/abi/selfPermitAbi/index.js.map +1 -1
- package/_cjs/evm/validate/zod.js +4 -3
- package/_cjs/evm/validate/zod.js.map +1 -1
- package/_cjs/generic/currency/price.js +23 -2
- package/_cjs/generic/currency/price.js.map +1 -1
- package/_cjs/generic/format/number.js +5 -1
- package/_cjs/generic/format/number.js.map +1 -1
- package/_cjs/generic/math/fraction.js +70 -3
- package/_cjs/generic/math/fraction.js.map +1 -1
- package/_cjs/generic/utils/id.js.map +1 -1
- package/_cjs/generic/utils/native-address.js.map +1 -1
- package/_cjs/generic/validate/zod.js +6 -0
- package/_cjs/generic/validate/zod.js.map +1 -1
- package/_cjs/mvm/index.js +1 -0
- package/_cjs/mvm/index.js.map +1 -1
- package/_cjs/mvm/validate/index.js +18 -0
- package/_cjs/mvm/validate/index.js.map +1 -0
- package/_cjs/mvm/validate/zod.js +48 -0
- package/_cjs/mvm/validate/zod.js.map +1 -0
- package/_cjs/stellar/config/simple-constants.js +1 -1
- package/_cjs/stellar/config/simple-constants.js.map +1 -1
- package/_cjs/stellar/index.js +1 -0
- package/_cjs/stellar/index.js.map +1 -1
- package/_cjs/stellar/validate/index.js +18 -0
- package/_cjs/stellar/validate/index.js.map +1 -0
- package/_cjs/stellar/validate/zod.js +60 -0
- package/_cjs/stellar/validate/zod.js.map +1 -0
- package/_cjs/svm/currency/token.js +1 -1
- package/_cjs/svm/currency/token.js.map +1 -1
- package/_cjs/svm/index.js +1 -0
- package/_cjs/svm/index.js.map +1 -1
- package/_cjs/svm/validate/index.js +18 -0
- package/_cjs/svm/validate/index.js.map +1 -0
- package/_cjs/svm/validate/zod.js +48 -0
- package/_cjs/svm/validate/zod.js.map +1 -0
- package/_cjs/version.js +1 -1
- package/_cjs/version.js.map +1 -1
- package/_esm/evm/abi/selfPermitAbi/index.js +0 -2
- package/_esm/evm/abi/selfPermitAbi/index.js.map +1 -1
- package/_esm/evm/validate/zod.js +4 -3
- package/_esm/evm/validate/zod.js.map +1 -1
- package/_esm/generic/currency/price.js +29 -2
- package/_esm/generic/currency/price.js.map +1 -1
- package/_esm/generic/format/number.js +5 -1
- package/_esm/generic/format/number.js.map +1 -1
- package/_esm/generic/math/fraction.js +70 -3
- package/_esm/generic/math/fraction.js.map +1 -1
- package/_esm/generic/utils/id.js.map +1 -1
- package/_esm/generic/utils/native-address.js.map +1 -1
- package/_esm/generic/validate/zod.js +6 -0
- package/_esm/generic/validate/zod.js.map +1 -1
- package/_esm/mvm/index.js +1 -0
- package/_esm/mvm/index.js.map +1 -1
- package/_esm/mvm/validate/index.js +2 -0
- package/_esm/mvm/validate/index.js.map +1 -0
- package/_esm/mvm/validate/zod.js +11 -0
- package/_esm/mvm/validate/zod.js.map +1 -0
- package/_esm/stellar/config/simple-constants.js +1 -1
- package/_esm/stellar/config/simple-constants.js.map +1 -1
- package/_esm/stellar/index.js +1 -0
- package/_esm/stellar/index.js.map +1 -1
- package/_esm/stellar/validate/index.js +2 -0
- package/_esm/stellar/validate/index.js.map +1 -0
- package/_esm/stellar/validate/zod.js +21 -0
- package/_esm/stellar/validate/zod.js.map +1 -0
- package/_esm/svm/currency/token.js +1 -1
- package/_esm/svm/currency/token.js.map +1 -1
- package/_esm/svm/index.js +1 -0
- package/_esm/svm/index.js.map +1 -1
- package/_esm/svm/validate/index.js +2 -0
- package/_esm/svm/validate/index.js.map +1 -0
- package/_esm/svm/validate/zod.js +11 -0
- package/_esm/svm/validate/zod.js.map +1 -0
- package/_esm/version.js +1 -1
- package/_esm/version.js.map +1 -1
- package/_types/evm/abi/selfPermitAbi/index.d.ts +0 -2
- package/_types/evm/abi/selfPermitAbi/index.d.ts.map +1 -1
- package/_types/evm/chain/chains.d.ts +10 -10
- package/_types/evm/validate/zod.d.ts.map +1 -1
- package/_types/generic/currency/price.d.ts +9 -2
- package/_types/generic/currency/price.d.ts.map +1 -1
- package/_types/generic/currency/serialized-currency.d.ts +1 -1
- package/_types/generic/math/fraction.d.ts +3 -1
- package/_types/generic/math/fraction.d.ts.map +1 -1
- package/_types/generic/types/for-chain.d.ts +2 -1
- package/_types/generic/types/for-chain.d.ts.map +1 -1
- package/_types/generic/utils/id.d.ts +4 -1
- package/_types/generic/utils/id.d.ts.map +1 -1
- package/_types/generic/utils/native-address.d.ts +2 -2
- package/_types/generic/utils/native-address.d.ts.map +1 -1
- package/_types/generic/validate/zod.d.ts +11 -0
- package/_types/generic/validate/zod.d.ts.map +1 -1
- package/_types/mvm/index.d.ts +1 -0
- package/_types/mvm/index.d.ts.map +1 -1
- package/_types/mvm/validate/index.d.ts +2 -0
- package/_types/mvm/validate/index.d.ts.map +1 -0
- package/_types/mvm/validate/zod.d.ts +6 -0
- package/_types/mvm/validate/zod.d.ts.map +1 -0
- package/_types/stellar/config/simple-constants.d.ts +1 -1
- package/_types/stellar/config/simple-constants.d.ts.map +1 -1
- package/_types/stellar/index.d.ts +1 -0
- package/_types/stellar/index.d.ts.map +1 -1
- package/_types/stellar/validate/index.d.ts +2 -0
- package/_types/stellar/validate/index.d.ts.map +1 -0
- package/_types/stellar/validate/zod.d.ts +10 -0
- package/_types/stellar/validate/zod.d.ts.map +1 -0
- package/_types/svm/config/tokens/tokens/USDC.d.ts +1 -1
- package/_types/svm/config/tokens/tokens/USDT.d.ts +1 -1
- package/_types/svm/config/tokens/tokens/WBTC.d.ts +1 -1
- package/_types/svm/config/tokens/tokens/WETH.d.ts +1 -1
- package/_types/svm/config/tokens/tokens/WSOL.d.ts +1 -1
- package/_types/svm/currency/currency.d.ts +2 -2
- package/_types/svm/currency/currency.d.ts.map +1 -1
- package/_types/svm/currency/token.d.ts +1 -1
- package/_types/svm/currency/token.d.ts.map +1 -1
- package/_types/svm/index.d.ts +1 -0
- package/_types/svm/index.d.ts.map +1 -1
- package/_types/svm/validate/index.d.ts +2 -0
- package/_types/svm/validate/index.d.ts.map +1 -0
- package/_types/svm/validate/zod.d.ts +7 -0
- package/_types/svm/validate/zod.d.ts.map +1 -0
- package/_types/version.d.ts +1 -1
- package/_types/version.d.ts.map +1 -1
- package/evm/abi/selfPermitAbi/index.ts +0 -2
- package/evm/pool/sushiswap-v3/entities/TickDataProvider.test.ts +4 -4
- package/evm/validate/zod.ts +4 -3
- package/generic/currency/price.test-d.ts +59 -0
- package/generic/currency/price.test.ts +132 -0
- package/generic/currency/price.ts +41 -3
- package/generic/format/number.test.ts +101 -1
- package/generic/format/number.ts +13 -6
- package/generic/math/fraction.test.ts +97 -0
- package/generic/math/fraction.ts +106 -3
- package/generic/types/for-chain.ts +16 -1
- package/generic/utils/id.test-d.ts +35 -0
- package/generic/utils/id.ts +8 -2
- package/generic/utils/native-address.test-d.ts +25 -0
- package/generic/utils/native-address.ts +6 -6
- package/generic/validate/zod.test.ts +44 -0
- package/generic/validate/zod.ts +6 -0
- package/mvm/index.ts +1 -0
- package/mvm/validate/index.ts +1 -0
- package/mvm/validate/zod.ts +13 -0
- package/package.json +3 -4
- package/stellar/config/simple-constants.ts +2 -5
- package/stellar/index.ts +1 -0
- package/stellar/validate/index.ts +1 -0
- package/stellar/validate/zod.ts +38 -0
- package/svm/currency/token.ts +1 -1
- package/svm/index.ts +1 -0
- package/svm/validate/index.ts +1 -0
- package/svm/validate/zod.ts +13 -0
- package/_cjs/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.js +0 -20
- package/_cjs/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.js.map +0 -1
- package/_cjs/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.js +0 -20
- package/_cjs/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.js.map +0 -1
- package/_cjs/evm/abi/splitter.js +0 -68
- package/_cjs/evm/abi/splitter.js.map +0 -1
- package/_cjs/evm/api/config.js +0 -20
- package/_cjs/evm/api/config.js.map +0 -1
- package/_cjs/evm/config/drpc.js +0 -83
- package/_cjs/evm/config/drpc.js.map +0 -1
- package/_cjs/evm/config/tokens/tokens/SGUSDC.js +0 -18
- package/_cjs/evm/config/tokens/tokens/SGUSDC.js.map +0 -1
- package/_esm/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.js +0 -17
- package/_esm/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.js.map +0 -1
- package/_esm/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.js +0 -17
- package/_esm/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.js.map +0 -1
- package/_esm/evm/abi/splitter.js +0 -34
- package/_esm/evm/abi/splitter.js.map +0 -1
- package/_esm/evm/api/config.js +0 -17
- package/_esm/evm/api/config.js.map +0 -1
- package/_esm/evm/config/drpc.js +0 -78
- package/_esm/evm/config/drpc.js.map +0 -1
- package/_esm/evm/config/tokens/tokens/SGUSDC.js +0 -15
- package/_esm/evm/config/tokens/tokens/SGUSDC.js.map +0 -1
- package/_types/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.d.ts +0 -32
- package/_types/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.d.ts.map +0 -1
- package/_types/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.d.ts +0 -32
- package/_types/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.d.ts.map +0 -1
- package/_types/evm/abi/splitter.d.ts +0 -2
- package/_types/evm/abi/splitter.d.ts.map +0 -1
- package/_types/evm/api/config.d.ts +0 -11
- package/_types/evm/api/config.d.ts.map +0 -1
- package/_types/evm/config/drpc.d.ts +0 -9
- package/_types/evm/config/drpc.d.ts.map +0 -1
- package/_types/evm/config/tokens/tokens/SGUSDC.d.ts +0 -9
- package/_types/evm/config/tokens/tokens/SGUSDC.d.ts.map +0 -1
- package/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitAllowedIfNecessary.ts +0 -16
- package/evm/abi/selfPermitAbi/selfPermitAbi_selfPermitIfNecessary.ts +0 -16
- package/evm/abi/splitter.ts +0 -53
- package/evm/api/config.ts +0 -23
- package/evm/config/drpc.ts +0 -92
- package/evm/config/tokens/tokens/SGUSDC.ts +0 -19
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { Price } from './price.js'
|
|
3
|
+
import { Token } from './token.js'
|
|
4
|
+
|
|
5
|
+
class TestToken extends Token {
|
|
6
|
+
public override toJSON() {
|
|
7
|
+
return {} as any
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public override wrap() {
|
|
11
|
+
return this
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const wbtc = new TestToken({
|
|
16
|
+
decimals: 8,
|
|
17
|
+
symbol: 'WBTC',
|
|
18
|
+
name: 'Wrapped Bitcoin',
|
|
19
|
+
chainId: 1,
|
|
20
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const usdc = new TestToken({
|
|
24
|
+
decimals: 6,
|
|
25
|
+
symbol: 'USDC',
|
|
26
|
+
name: 'USD Coin',
|
|
27
|
+
chainId: 1,
|
|
28
|
+
address: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe('Price', () => {
|
|
32
|
+
describe('fromHuman', () => {
|
|
33
|
+
it('creates a price for currencies with different decimals', () => {
|
|
34
|
+
const price = Price.fromHuman(wbtc, usdc, '1.25')
|
|
35
|
+
|
|
36
|
+
expect(price.base).toBe(wbtc)
|
|
37
|
+
expect(price.quote).toBe(usdc)
|
|
38
|
+
expect(price.toNumber()).toBe(1.25)
|
|
39
|
+
expect(price.numerator).toBe(125000000n)
|
|
40
|
+
expect(price.denominator).toBe(10000000000n)
|
|
41
|
+
expect(price.invert().toNumber()).toBe(0.8)
|
|
42
|
+
expect(price.invert().toString({ maxFixed: 18 })).toBe('0.8')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('accepts a decimal without a leading zero', () => {
|
|
46
|
+
expect(Price.fromHuman(wbtc, usdc, '.5').toNumber()).toBe(0.5)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('accepts zero', () => {
|
|
50
|
+
const price = Price.fromHuman(wbtc, usdc, '0')
|
|
51
|
+
|
|
52
|
+
expect(price.toNumber()).toBe(0)
|
|
53
|
+
expect(price.numerator).toBe(0n)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('preserves more than 18 fractional digits', () => {
|
|
57
|
+
const price = Price.fromHuman(wbtc, usdc, '0.0000000000000000001')
|
|
58
|
+
|
|
59
|
+
expect(price.numerator).toBe(1000000n)
|
|
60
|
+
expect(price.denominator).toBe(1000000000000000000000000000n)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it.each([
|
|
64
|
+
'',
|
|
65
|
+
'-1',
|
|
66
|
+
'1e6',
|
|
67
|
+
'Infinity',
|
|
68
|
+
])('rejects invalid input %j', (value) => {
|
|
69
|
+
expect(() => Price.fromHuman(wbtc, usdc, value)).toThrow(
|
|
70
|
+
`Invalid price: ${value}`,
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('tryFromHuman', () => {
|
|
76
|
+
it('returns a price for valid input', () => {
|
|
77
|
+
expect(Price.tryFromHuman(wbtc, usdc, '1.25')?.toNumber()).toBe(1.25)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it.each([
|
|
81
|
+
'',
|
|
82
|
+
'-1',
|
|
83
|
+
'1e6',
|
|
84
|
+
'Infinity',
|
|
85
|
+
])('returns undefined for invalid input %j', (value) => {
|
|
86
|
+
expect(Price.tryFromHuman(wbtc, usdc, value)).toBeUndefined()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('string formatting', () => {
|
|
91
|
+
it('formats very small prices without scientific notation', () => {
|
|
92
|
+
const price = Price.fromHuman(wbtc, usdc, '0.000000000001')
|
|
93
|
+
|
|
94
|
+
expect(price.toString({ maxFixed: 18 })).toBe('0.000000000001')
|
|
95
|
+
expect(price.toString({ significant: 6 })).toBe('0.000000000001')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('truncates repeating prices to maxFixed decimal places', () => {
|
|
99
|
+
const price = new Price({
|
|
100
|
+
base: wbtc,
|
|
101
|
+
quote: wbtc,
|
|
102
|
+
numerator: 1n,
|
|
103
|
+
denominator: 3n,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
expect(price.toString({ maxFixed: 18 })).toBe('0.333333333333333333')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('preserves prices beyond Number.MAX_SAFE_INTEGER', () => {
|
|
110
|
+
const price = new Price({
|
|
111
|
+
base: wbtc,
|
|
112
|
+
quote: wbtc,
|
|
113
|
+
numerator: 9007199254740993n,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(price.toString({ maxFixed: 18 })).toBe('9007199254740993')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('supports fixed, maxFixed, and significant formatting', () => {
|
|
120
|
+
const price = new Price({
|
|
121
|
+
base: wbtc,
|
|
122
|
+
quote: wbtc,
|
|
123
|
+
numerator: 1n,
|
|
124
|
+
denominator: 8n,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
expect(price.toString({ fixed: 3 })).toBe('0.125')
|
|
128
|
+
expect(price.toString({ maxFixed: 2 })).toBe('0.12')
|
|
129
|
+
expect(price.toString({ significant: 2 })).toBe('0.13')
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
})
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { numberToFixed } from '../format/number.js'
|
|
2
1
|
import { Fraction } from '../math/fraction.js'
|
|
3
2
|
import type { BigintIsh } from '../types/bigintish.js'
|
|
4
3
|
import { Amount } from './amount.js'
|
|
@@ -11,6 +10,45 @@ export class Price<
|
|
|
11
10
|
public readonly base: TBase
|
|
12
11
|
public readonly quote: TQuote
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Creates a Price from a human-readable quote-per-base value, e.g. "1.5".
|
|
15
|
+
*/
|
|
16
|
+
public static fromHuman<TBase extends Currency, TQuote extends Currency>(
|
|
17
|
+
base: TBase,
|
|
18
|
+
quote: TQuote,
|
|
19
|
+
value: string,
|
|
20
|
+
): Price<TBase, TQuote> {
|
|
21
|
+
if (!value.match(/^\d*\.?\d+$/)) {
|
|
22
|
+
throw new Error(`Invalid price: ${value}`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const [whole = '', fraction = ''] = value.split('.')
|
|
26
|
+
const decimalScale = 10n ** BigInt(fraction.length)
|
|
27
|
+
const valueWithoutDecimals = BigInt(`${whole}${fraction}`)
|
|
28
|
+
|
|
29
|
+
return new Price({
|
|
30
|
+
base,
|
|
31
|
+
quote,
|
|
32
|
+
numerator: valueWithoutDecimals * 10n ** BigInt(quote.decimals),
|
|
33
|
+
denominator: decimalScale * 10n ** BigInt(base.decimals),
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tries to create a Price from a human-readable quote-per-base value.
|
|
39
|
+
*/
|
|
40
|
+
public static tryFromHuman<TBase extends Currency, TQuote extends Currency>(
|
|
41
|
+
base: TBase,
|
|
42
|
+
quote: TQuote,
|
|
43
|
+
value: string,
|
|
44
|
+
): Price<TBase, TQuote> | undefined {
|
|
45
|
+
try {
|
|
46
|
+
return Price.fromHuman(base, quote, value)
|
|
47
|
+
} catch {
|
|
48
|
+
return undefined
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
14
52
|
constructor(
|
|
15
53
|
args:
|
|
16
54
|
| {
|
|
@@ -65,8 +103,8 @@ export class Price<
|
|
|
65
103
|
return super.mul(this.scalar).toNumber()
|
|
66
104
|
}
|
|
67
105
|
|
|
68
|
-
public override toString(args: Parameters<
|
|
69
|
-
return
|
|
106
|
+
public override toString(args: Parameters<Fraction['toString']>[0]): string {
|
|
107
|
+
return super.mul(this.scalar).toString(args)
|
|
70
108
|
}
|
|
71
109
|
|
|
72
110
|
public override toSignificant(significantDigits = 5): string {
|
|
@@ -127,7 +127,7 @@ describe('numberToFixed', () => {
|
|
|
127
127
|
})
|
|
128
128
|
|
|
129
129
|
describe('edge cases', () => {
|
|
130
|
-
it('should handle empty and
|
|
130
|
+
it('should handle empty and zero values', () => {
|
|
131
131
|
expect(numberToFixed(0, { maxFixed: 2 })).toBe('0')
|
|
132
132
|
expect(numberToFixed('0', { maxFixed: 2 })).toBe('0')
|
|
133
133
|
expect(numberToFixed('0.00', { maxFixed: 2 })).toBe('0')
|
|
@@ -143,4 +143,104 @@ describe('numberToFixed', () => {
|
|
|
143
143
|
expect(numberToFixed('1.23e+2', { maxFixed: 2 })).toBe('123')
|
|
144
144
|
})
|
|
145
145
|
})
|
|
146
|
+
|
|
147
|
+
describe('string rounding edge cases', () => {
|
|
148
|
+
it('should round fixed-decimal string inputs that overflow into a new integer digit', () => {
|
|
149
|
+
expect(numberToFixed('9.995', { fixed: 2 })).toBe('10')
|
|
150
|
+
expect(numberToFixed('-9.995', { fixed: 2 })).toBe('-10')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should round long leading-zero decimals when using significant digits', () => {
|
|
154
|
+
expect(numberToFixed('0.00009999', { significant: 2 })).toBe('0.0001')
|
|
155
|
+
expect(numberToFixed('-0.00009999', { significant: 2 })).toBe('-0.0001')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should round large negative strings when all significant digits are 9s', () => {
|
|
159
|
+
expect(numberToFixed('-9999.9', { significant: 1 })).toBe('-10000')
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
describe('fixed rounding cascades', () => {
|
|
164
|
+
it('should cascade rounding across multiple integer digits for strings', () => {
|
|
165
|
+
expect(numberToFixed('199.995', { fixed: 2 })).toBe('200')
|
|
166
|
+
expect(numberToFixed('-199.995', { fixed: 2 })).toBe('-200')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should keep trailing zeros for numeric inputs when the same rounding occurs', () => {
|
|
170
|
+
expect(numberToFixed(199.995, { fixed: 2 })).toBe('200.00')
|
|
171
|
+
expect(numberToFixed(-199.995, { fixed: 2 })).toBe('-200.00')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should handle +/-0.005 thresholds symmetrically', () => {
|
|
175
|
+
expect(numberToFixed('0.005', { fixed: 2 })).toBe('0.01')
|
|
176
|
+
expect(numberToFixed('-0.005', { fixed: 2 })).toBe('-0.01')
|
|
177
|
+
expect(numberToFixed(0.005, { fixed: 2 })).toBe('0.01')
|
|
178
|
+
expect(numberToFixed(-0.005, { fixed: 2 })).toBe('-0.01')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should preserve decimal accuracy for strings at IEEE rounding pain points', () => {
|
|
182
|
+
expect(numberToFixed('2.675', { fixed: 2 })).toBe('2.68')
|
|
183
|
+
expect(numberToFixed(2.675, { fixed: 2 })).toBe('2.67')
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe('maxFixed nuances', () => {
|
|
188
|
+
it('should truncate (not round) string inputs for maxFixed', () => {
|
|
189
|
+
expect(numberToFixed('2.999', { maxFixed: 2 })).toBe('2.99')
|
|
190
|
+
expect(numberToFixed('-2.999', { maxFixed: 2 })).toBe('-2.99')
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should round numeric inputs for maxFixed after trimming trailing zeros', () => {
|
|
194
|
+
expect(numberToFixed(2.999, { maxFixed: 2 })).toBe('3')
|
|
195
|
+
expect(numberToFixed(-2.999, { maxFixed: 2 })).toBe('-3')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should drop redundant zeros for very small fractions supplied as strings', () => {
|
|
199
|
+
expect(numberToFixed('0.0001000', { maxFixed: 6 })).toBe('0.0001')
|
|
200
|
+
expect(numberToFixed('-0.0001000', { maxFixed: 6 })).toBe('-0.0001')
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should distinguish between truncation and rounding when maxFixed is zero', () => {
|
|
204
|
+
expect(numberToFixed('12.9', { maxFixed: 0 })).toBe('12')
|
|
205
|
+
expect(numberToFixed('-12.9', { maxFixed: 0 })).toBe('-12')
|
|
206
|
+
expect(numberToFixed(12.9, { maxFixed: 0 })).toBe('13')
|
|
207
|
+
expect(numberToFixed(-12.9, { maxFixed: 0 })).toBe('-13')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('should trim or round repeating decimals based on input type', () => {
|
|
211
|
+
expect(numberToFixed('0.10999', { maxFixed: 3 })).toBe('0.109')
|
|
212
|
+
expect(numberToFixed('-0.10999', { maxFixed: 3 })).toBe('-0.109')
|
|
213
|
+
expect(numberToFixed(0.10999, { maxFixed: 3 })).toBe('0.11')
|
|
214
|
+
expect(numberToFixed(-0.10999, { maxFixed: 3 })).toBe('-0.11')
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
describe('significant digits with decimals', () => {
|
|
219
|
+
it('should keep track of leading zeros for negative decimals', () => {
|
|
220
|
+
expect(numberToFixed('-0.00056789', { significant: 2 })).toBe('-0.0006')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should round decimals just below one down to -1 with a single significant digit', () => {
|
|
224
|
+
expect(numberToFixed('-0.9994', { significant: 1 })).toBe('-1')
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('input validation', () => {
|
|
229
|
+
it('should accept trailing decimal strings', () => {
|
|
230
|
+
expect(numberToFixed('1.', { fixed: 2 })).toBe('1.00')
|
|
231
|
+
expect(numberToFixed('1.', { significant: 2 })).toBe('1')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('should reject invalid numeric strings', () => {
|
|
235
|
+
expect(() => numberToFixed('invalid', { fixed: 2 })).toThrow(
|
|
236
|
+
'Invalid number string',
|
|
237
|
+
)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should reject empty option objects', () => {
|
|
241
|
+
expect(() => numberToFixed(1, {} as any)).toThrow(
|
|
242
|
+
'Invalid arguments for numberToFixed',
|
|
243
|
+
)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
146
246
|
})
|
package/generic/format/number.ts
CHANGED
|
@@ -175,7 +175,9 @@ function stringToFixed(
|
|
|
175
175
|
significantDecimal = significantDecimal.slice(0, -1)
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
const res = `${integerPart}${
|
|
178
|
+
const res = `${integerPart}${
|
|
179
|
+
significantDecimal ? `.${significantDecimal}` : ''
|
|
180
|
+
}`
|
|
179
181
|
return roundString(str, res)
|
|
180
182
|
}
|
|
181
183
|
|
|
@@ -192,6 +194,10 @@ function stringToFixed(
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
function roundString(original: string, formatted: string) {
|
|
197
|
+
if (!original.match(/^-?(?:\d+\.?|\d*\.\d+)$/)) {
|
|
198
|
+
throw new Error('Invalid number string')
|
|
199
|
+
}
|
|
200
|
+
|
|
195
201
|
const decimalIndex = original.indexOf('.')
|
|
196
202
|
|
|
197
203
|
const nextDigitIndex = formatted.length
|
|
@@ -216,15 +222,16 @@ function roundString(original: string, formatted: string) {
|
|
|
216
222
|
// handle full carry (e.g., "9.9" -> "10")
|
|
217
223
|
if (formatted.length === 0 || formatted === '-') {
|
|
218
224
|
const negative = original.startsWith('-')
|
|
219
|
-
const
|
|
225
|
+
const digitsLeftOfDecimal = decimalIndex - (negative ? 1 : 0)
|
|
226
|
+
const zeros = Math.max(digitsLeftOfDecimal, 0)
|
|
220
227
|
return `${negative ? '-' : ''}1${'0'.repeat(zeros)}`
|
|
221
228
|
}
|
|
222
229
|
}
|
|
223
230
|
|
|
224
|
-
return `${formatted.substring(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
231
|
+
return `${formatted.substring(
|
|
232
|
+
0,
|
|
233
|
+
formatted.length - 1,
|
|
234
|
+
)}${String.fromCharCode(lastDigit() + 1)}`.padEnd(decimalIndex, '0')
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
return formatted
|
|
@@ -188,6 +188,103 @@ describe('Fraction', () => {
|
|
|
188
188
|
})
|
|
189
189
|
})
|
|
190
190
|
|
|
191
|
+
describe('string formatting', () => {
|
|
192
|
+
it('formats terminating fractions without floating-point artifacts', () => {
|
|
193
|
+
const fraction = new Fraction({ numerator: 4n, denominator: 5n })
|
|
194
|
+
|
|
195
|
+
expect(fraction.toString({ maxFixed: 18 })).toBe('0.8')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('truncates repeating fractions to maxFixed decimal places', () => {
|
|
199
|
+
const fraction = new Fraction({ numerator: 1n, denominator: 3n })
|
|
200
|
+
|
|
201
|
+
expect(fraction.toString({ maxFixed: 18 })).toBe('0.333333333333333333')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('preserves very small fractions in ordinary decimal notation', () => {
|
|
205
|
+
const fraction = new Fraction({
|
|
206
|
+
numerator: 1n,
|
|
207
|
+
denominator: 10n ** 12n,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
expect(fraction.toString({ maxFixed: 18 })).toBe('0.000000000001')
|
|
211
|
+
expect(fraction.toSignificant(6)).toBe('0.000000000001')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('preserves integers beyond Number.MAX_SAFE_INTEGER', () => {
|
|
215
|
+
const fraction = new Fraction({
|
|
216
|
+
numerator: 9007199254740993n,
|
|
217
|
+
denominator: 1n,
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
expect(fraction.toString({ maxFixed: 18 })).toBe('9007199254740993')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('rounds and pads fixed decimal places', () => {
|
|
224
|
+
expect(
|
|
225
|
+
new Fraction({ numerator: 1n, denominator: 8n }).toString({
|
|
226
|
+
fixed: 2,
|
|
227
|
+
}),
|
|
228
|
+
).toBe('0.13')
|
|
229
|
+
expect(
|
|
230
|
+
new Fraction({ numerator: 1n, denominator: 2n }).toString({
|
|
231
|
+
fixed: 3,
|
|
232
|
+
}),
|
|
233
|
+
).toBe('0.500')
|
|
234
|
+
expect(
|
|
235
|
+
new Fraction({ numerator: 199995n, denominator: 1000n }).toString({
|
|
236
|
+
fixed: 2,
|
|
237
|
+
}),
|
|
238
|
+
).toBe('200.00')
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('rounds to significant digits without scientific notation', () => {
|
|
242
|
+
expect(
|
|
243
|
+
new Fraction({ numerator: 1n, denominator: 3n }).toString({
|
|
244
|
+
significant: 6,
|
|
245
|
+
}),
|
|
246
|
+
).toBe('0.333333')
|
|
247
|
+
expect(
|
|
248
|
+
new Fraction({
|
|
249
|
+
numerator: 123456789123n,
|
|
250
|
+
denominator: 100n,
|
|
251
|
+
}).toString({ significant: 4 }),
|
|
252
|
+
).toBe('1235000000')
|
|
253
|
+
expect(
|
|
254
|
+
new Fraction({ numerator: 9999n, denominator: 10n }).toString({
|
|
255
|
+
significant: 3,
|
|
256
|
+
}),
|
|
257
|
+
).toBe('1000')
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('formats negative fractions', () => {
|
|
261
|
+
const fraction = new Fraction({ numerator: -1n, denominator: 8n })
|
|
262
|
+
|
|
263
|
+
expect(fraction.toString({ fixed: 2 })).toBe('-0.13')
|
|
264
|
+
expect(fraction.toString({ maxFixed: 2 })).toBe('-0.12')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('formats zero-denominator fractions as non-finite numbers', () => {
|
|
268
|
+
const infinity = new Fraction({ numerator: 1n, denominator: 2n }).div(
|
|
269
|
+
new Fraction({ numerator: 0n }),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
expect(infinity.toString({ fixed: 2 })).toBe('Infinity')
|
|
273
|
+
expect(infinity.toString({ maxFixed: 2 })).toBe('Infinity')
|
|
274
|
+
expect(infinity.toString({ significant: 2 })).toBe('Infinity')
|
|
275
|
+
expect(
|
|
276
|
+
new Fraction({ numerator: -1n, denominator: 0n }).toString({
|
|
277
|
+
maxFixed: 2,
|
|
278
|
+
}),
|
|
279
|
+
).toBe('-Infinity')
|
|
280
|
+
expect(
|
|
281
|
+
new Fraction({ numerator: 0n, denominator: 0n }).toString({
|
|
282
|
+
maxFixed: 2,
|
|
283
|
+
}),
|
|
284
|
+
).toBe('NaN')
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
191
288
|
describe('serialization', () => {
|
|
192
289
|
it('should serialize to JSON correctly', () => {
|
|
193
290
|
const fraction = new Fraction({ numerator: 3, denominator: 4 })
|
package/generic/math/fraction.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import invariant from 'tiny-invariant'
|
|
2
|
-
import { numberToFixed } from '../format/number.js'
|
|
2
|
+
import type { numberToFixed } from '../format/number.js'
|
|
3
3
|
import type { BigintIsh } from '../types/bigintish.js'
|
|
4
4
|
|
|
5
5
|
export class Fraction {
|
|
@@ -164,12 +164,115 @@ export class Fraction {
|
|
|
164
164
|
return Number(this.numerator) / Number(this.denominator)
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
private formatDecimal(
|
|
168
|
+
decimalPlaces: number,
|
|
169
|
+
{ round, trim }: { round: boolean; trim: boolean },
|
|
170
|
+
): string {
|
|
171
|
+
invariant(
|
|
172
|
+
Number.isSafeInteger(decimalPlaces),
|
|
173
|
+
'Decimal places must be a safe integer',
|
|
174
|
+
)
|
|
175
|
+
invariant(this.denominator !== 0n, 'Cannot format a fraction by zero')
|
|
176
|
+
|
|
177
|
+
const negative = this.numerator < 0n !== this.denominator < 0n
|
|
178
|
+
const numerator = this.numerator < 0n ? -this.numerator : this.numerator
|
|
179
|
+
const denominator =
|
|
180
|
+
this.denominator < 0n ? -this.denominator : this.denominator
|
|
181
|
+
|
|
182
|
+
const scale = 10n ** BigInt(Math.abs(decimalPlaces))
|
|
183
|
+
const scaledDenominator =
|
|
184
|
+
decimalPlaces >= 0 ? denominator : denominator * scale
|
|
185
|
+
const scaledNumerator = decimalPlaces >= 0 ? numerator * scale : numerator
|
|
186
|
+
|
|
187
|
+
let scaledValue = scaledNumerator / scaledDenominator
|
|
188
|
+
const remainder = scaledNumerator % scaledDenominator
|
|
189
|
+
if (round && remainder * 2n >= scaledDenominator) {
|
|
190
|
+
scaledValue += 1n
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let integerPart: string
|
|
194
|
+
let decimalPart = ''
|
|
195
|
+
if (decimalPlaces > 0) {
|
|
196
|
+
const value = scaledValue.toString().padStart(decimalPlaces + 1, '0')
|
|
197
|
+
integerPart = value.slice(0, -decimalPlaces)
|
|
198
|
+
decimalPart = value.slice(-decimalPlaces)
|
|
199
|
+
} else {
|
|
200
|
+
integerPart = (scaledValue * scale).toString()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (trim) {
|
|
204
|
+
decimalPart = decimalPart.replace(/0+$/, '')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return `${negative ? '-' : ''}${integerPart}${
|
|
208
|
+
decimalPart ? `.${decimalPart}` : ''
|
|
209
|
+
}`
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private getDecimalExponent(): number {
|
|
213
|
+
const numerator = this.numerator < 0n ? -this.numerator : this.numerator
|
|
214
|
+
const denominator =
|
|
215
|
+
this.denominator < 0n ? -this.denominator : this.denominator
|
|
216
|
+
|
|
217
|
+
invariant(numerator !== 0n, 'Cannot get the decimal exponent of zero')
|
|
218
|
+
invariant(denominator !== 0n, 'Cannot format a fraction by zero')
|
|
219
|
+
|
|
220
|
+
let exponent = numerator.toString().length - denominator.toString().length
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
(exponent >= 0 && numerator < denominator * 10n ** BigInt(exponent)) ||
|
|
224
|
+
(exponent < 0 && numerator * 10n ** BigInt(-exponent) < denominator)
|
|
225
|
+
) {
|
|
226
|
+
exponent -= 1
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return exponent
|
|
230
|
+
}
|
|
231
|
+
|
|
167
232
|
public toString(args: Parameters<typeof numberToFixed>[1]): string {
|
|
168
|
-
|
|
233
|
+
if ('fixed' in args) {
|
|
234
|
+
invariant(
|
|
235
|
+
Number.isSafeInteger(args.fixed) && args.fixed >= 0,
|
|
236
|
+
'Fixed digits must be a non-negative safe integer',
|
|
237
|
+
)
|
|
238
|
+
} else if ('maxFixed' in args) {
|
|
239
|
+
invariant(
|
|
240
|
+
Number.isSafeInteger(args.maxFixed) && args.maxFixed >= 0,
|
|
241
|
+
'Maximum fixed digits must be a non-negative safe integer',
|
|
242
|
+
)
|
|
243
|
+
} else if ('significant' in args) {
|
|
244
|
+
invariant(
|
|
245
|
+
Number.isSafeInteger(args.significant) && args.significant > 0,
|
|
246
|
+
'Significant digits must be a positive safe integer',
|
|
247
|
+
)
|
|
248
|
+
} else {
|
|
249
|
+
throw new Error('Invalid arguments for Fraction.toString')
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (this.denominator === 0n) {
|
|
253
|
+
return this.toNumber().toString()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if ('fixed' in args) {
|
|
257
|
+
return this.formatDecimal(args.fixed, { round: true, trim: false })
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if ('maxFixed' in args) {
|
|
261
|
+
return this.formatDecimal(args.maxFixed, { round: false, trim: true })
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if ('significant' in args) {
|
|
265
|
+
if (this.numerator === 0n) return '0'
|
|
266
|
+
|
|
267
|
+
const decimalPlaces = args.significant - this.getDecimalExponent() - 1
|
|
268
|
+
return this.formatDecimal(decimalPlaces, { round: true, trim: true })
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
throw new Error('Invalid arguments for Fraction.toString')
|
|
169
272
|
}
|
|
170
273
|
|
|
171
274
|
public toSignificant(significantDigits: number): string {
|
|
172
|
-
return this.
|
|
275
|
+
return this.toString({ significant: significantDigits })
|
|
173
276
|
}
|
|
174
277
|
|
|
175
278
|
public toJSON() {
|
|
@@ -13,7 +13,11 @@ import type {
|
|
|
13
13
|
MvmToken,
|
|
14
14
|
MvmTxHash,
|
|
15
15
|
} from '../../mvm/currency/token.js'
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
StellarAddress,
|
|
18
|
+
StellarContractAddress,
|
|
19
|
+
StellarTxHash,
|
|
20
|
+
} from '../../stellar/address.js'
|
|
17
21
|
import type { StellarChainId } from '../../stellar/chain/chains.js'
|
|
18
22
|
import type { StellarCurrency } from '../../stellar/currency/currency.js'
|
|
19
23
|
import type { StellarToken } from '../../stellar/currency/token.js'
|
|
@@ -82,6 +86,17 @@ export type AddressFor<TChainId extends ChainId> = TChainId extends EvmChainId
|
|
|
82
86
|
? StellarAddress
|
|
83
87
|
: never
|
|
84
88
|
|
|
89
|
+
export type ContractAddressFor<TChainId extends ChainId> =
|
|
90
|
+
TChainId extends EvmChainId
|
|
91
|
+
? EvmAddress
|
|
92
|
+
: TChainId extends MvmChainId
|
|
93
|
+
? MvmAddress
|
|
94
|
+
: TChainId extends SvmChainId
|
|
95
|
+
? SvmAddress
|
|
96
|
+
: TChainId extends StellarChainId
|
|
97
|
+
? StellarContractAddress
|
|
98
|
+
: never
|
|
99
|
+
|
|
85
100
|
export type TxHashFor<TChainId extends ChainId> = TChainId extends EvmChainId
|
|
86
101
|
? EvmTxHash
|
|
87
102
|
: TChainId extends MvmChainId
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { SvmChainId } from '~/svm/chain/chains.js'
|
|
3
|
+
import type { SvmAddress } from '~/svm/currency/token.js'
|
|
4
|
+
import type { EvmChainId } from '../../evm/chain/chains.js'
|
|
5
|
+
import type { EvmAddress } from '../../evm/currency/token.js'
|
|
6
|
+
import type { ID } from '../types/id.js'
|
|
7
|
+
import { getChainIdAddressFromId, getIdFromChainIdAddress } from './id.js'
|
|
8
|
+
|
|
9
|
+
describe('getChainIdAddressFromId', () => {
|
|
10
|
+
it('preserves EVM chainId and address types', () => {
|
|
11
|
+
const id = '1:0xaa' as ID<EvmChainId, EvmAddress>
|
|
12
|
+
|
|
13
|
+
expectTypeOf(getChainIdAddressFromId(id)).toEqualTypeOf<{
|
|
14
|
+
chainId: EvmChainId
|
|
15
|
+
address: EvmAddress
|
|
16
|
+
}>()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('preserves Solana chainId and address types', () => {
|
|
20
|
+
const id = getIdFromChainIdAddress(
|
|
21
|
+
SvmChainId.SOLANA,
|
|
22
|
+
'So11111111111111111111111111111111111111112' as SvmAddress,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
expectTypeOf(getChainIdAddressFromId(id)).not.toEqualTypeOf<{
|
|
26
|
+
chainId: SvmChainId
|
|
27
|
+
address: `${SvmAddress}`
|
|
28
|
+
}>()
|
|
29
|
+
|
|
30
|
+
expectTypeOf(getChainIdAddressFromId(id)).toEqualTypeOf<{
|
|
31
|
+
chainId: SvmChainId
|
|
32
|
+
address: SvmAddress
|
|
33
|
+
}>()
|
|
34
|
+
})
|
|
35
|
+
})
|
package/generic/utils/id.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import type { SvmAddress } from '../../svm/currency/token.js'
|
|
1
2
|
import type { ChainId } from '../chain/chains.js'
|
|
2
3
|
import type { AddressFor } from '../types/for-chain.js'
|
|
3
4
|
import type { ID } from '../types/id.js'
|
|
4
5
|
import { getNativeAddress } from './native-address.js'
|
|
5
6
|
import { normalizeAddress } from './normalize-address.js'
|
|
6
7
|
|
|
8
|
+
// Hacky fix, SvmAddress is a branded type, but when used in a template literal type, it loses its branding.
|
|
9
|
+
type ReturnedAddress<TAddress extends string> = TAddress extends `${SvmAddress}`
|
|
10
|
+
? SvmAddress
|
|
11
|
+
: TAddress
|
|
12
|
+
|
|
7
13
|
export function getChainIdAddressFromId<
|
|
8
14
|
TChainId extends ChainId,
|
|
9
15
|
TAddress extends string,
|
|
@@ -11,7 +17,7 @@ export function getChainIdAddressFromId<
|
|
|
11
17
|
id: ID<TChainId, TAddress>,
|
|
12
18
|
): {
|
|
13
19
|
chainId: TChainId
|
|
14
|
-
address: TAddress
|
|
20
|
+
address: ReturnedAddress<TAddress>
|
|
15
21
|
} {
|
|
16
22
|
const separatorIndex = id.indexOf(':')
|
|
17
23
|
|
|
@@ -22,7 +28,7 @@ export function getChainIdAddressFromId<
|
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
const chainId = +id.slice(0, separatorIndex) as TChainId
|
|
25
|
-
const address = id.slice(separatorIndex + 1) as TAddress
|
|
31
|
+
const address = id.slice(separatorIndex + 1) as ReturnedAddress<TAddress>
|
|
26
32
|
|
|
27
33
|
if (!chainId || !address) {
|
|
28
34
|
throw new Error(
|