@tcswap/helpers 4.5.15
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/api/index.cjs +4 -0
- package/dist/api/index.cjs.map +16 -0
- package/dist/api/index.js +4 -0
- package/dist/api/index.js.map +16 -0
- package/dist/chunk-pfmeq01a.js +5 -0
- package/dist/chunk-pfmeq01a.js.map +9 -0
- package/dist/chunk-vb4wtm2w.js +4 -0
- package/dist/chunk-vb4wtm2w.js.map +9 -0
- package/dist/contracts.cjs +4 -0
- package/dist/contracts.cjs.map +10 -0
- package/dist/contracts.js +4 -0
- package/dist/contracts.js.map +10 -0
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +30 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +30 -0
- package/dist/tokens.cjs +4 -0
- package/dist/tokens.cjs.map +10 -0
- package/dist/tokens.js +4 -0
- package/dist/tokens.js.map +10 -0
- package/dist/types/api/index.d.ts +502 -0
- package/dist/types/api/index.d.ts.map +1 -0
- package/dist/types/api/memoless/endpoints.d.ts +56 -0
- package/dist/types/api/memoless/endpoints.d.ts.map +1 -0
- package/dist/types/api/memoless/types.d.ts +85 -0
- package/dist/types/api/memoless/types.d.ts.map +1 -0
- package/dist/types/api/midgard/endpoints.d.ts +80 -0
- package/dist/types/api/midgard/endpoints.d.ts.map +1 -0
- package/dist/types/api/midgard/types.d.ts +543 -0
- package/dist/types/api/midgard/types.d.ts.map +1 -0
- package/dist/types/api/thornode/endpoints.d.ts +34 -0
- package/dist/types/api/thornode/endpoints.d.ts.map +1 -0
- package/dist/types/api/thornode/types.d.ts +264 -0
- package/dist/types/api/thornode/types.d.ts.map +1 -0
- package/dist/types/api/uswap/endpoints.d.ts +372 -0
- package/dist/types/api/uswap/endpoints.d.ts.map +1 -0
- package/dist/types/api/uswap/types.d.ts +1487 -0
- package/dist/types/api/uswap/types.d.ts.map +1 -0
- package/dist/types/contracts.d.ts +2 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/index.d.ts +32 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/modules/assetValue.d.ts +82 -0
- package/dist/types/modules/assetValue.d.ts.map +1 -0
- package/dist/types/modules/bigIntArithmetics.d.ts +60 -0
- package/dist/types/modules/bigIntArithmetics.d.ts.map +1 -0
- package/dist/types/modules/feeMultiplier.d.ts +48 -0
- package/dist/types/modules/feeMultiplier.d.ts.map +1 -0
- package/dist/types/modules/requestClient.d.ts +33 -0
- package/dist/types/modules/requestClient.d.ts.map +1 -0
- package/dist/types/modules/uSwapConfig.d.ts +249 -0
- package/dist/types/modules/uSwapConfig.d.ts.map +1 -0
- package/dist/types/modules/uSwapError.d.ts +879 -0
- package/dist/types/modules/uSwapError.d.ts.map +1 -0
- package/dist/types/modules/uSwapNumber.d.ts +10 -0
- package/dist/types/modules/uSwapNumber.d.ts.map +1 -0
- package/dist/types/tokens.d.ts +2 -0
- package/dist/types/tokens.d.ts.map +1 -0
- package/dist/types/types/commonTypes.d.ts +16 -0
- package/dist/types/types/commonTypes.d.ts.map +1 -0
- package/dist/types/types/derivationPath.d.ts +4 -0
- package/dist/types/types/derivationPath.d.ts.map +1 -0
- package/dist/types/types/errors/apiV1.d.ts +2 -0
- package/dist/types/types/errors/apiV1.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +6 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/types/quotes.d.ts +180 -0
- package/dist/types/types/quotes.d.ts.map +1 -0
- package/dist/types/types/sdk.d.ts +35 -0
- package/dist/types/types/sdk.d.ts.map +1 -0
- package/dist/types/types/wallet.d.ts +130 -0
- package/dist/types/types/wallet.d.ts.map +1 -0
- package/dist/types/utils/asset.d.ts +37 -0
- package/dist/types/utils/asset.d.ts.map +1 -0
- package/dist/types/utils/chains.d.ts +13 -0
- package/dist/types/utils/chains.d.ts.map +1 -0
- package/dist/types/utils/derivationPath.d.ts +21 -0
- package/dist/types/utils/derivationPath.d.ts.map +1 -0
- package/dist/types/utils/explorerUrls.d.ts +10 -0
- package/dist/types/utils/explorerUrls.d.ts.map +1 -0
- package/dist/types/utils/liquidity.d.ts +62 -0
- package/dist/types/utils/liquidity.d.ts.map +1 -0
- package/dist/types/utils/memo.d.ts +65 -0
- package/dist/types/utils/memo.d.ts.map +1 -0
- package/dist/types/utils/others.d.ts +15 -0
- package/dist/types/utils/others.d.ts.map +1 -0
- package/dist/types/utils/validators.d.ts +6 -0
- package/dist/types/utils/validators.d.ts.map +1 -0
- package/dist/types/utils/wallets.d.ts +36 -0
- package/dist/types/utils/wallets.d.ts.map +1 -0
- package/package.json +67 -0
- package/src/api/index.ts +15 -0
- package/src/api/memoless/endpoints.ts +62 -0
- package/src/api/memoless/types.ts +83 -0
- package/src/api/midgard/endpoints.ts +352 -0
- package/src/api/midgard/types.ts +515 -0
- package/src/api/thornode/endpoints.ts +109 -0
- package/src/api/thornode/types.ts +247 -0
- package/src/api/uswap/endpoints.ts +252 -0
- package/src/api/uswap/types.ts +626 -0
- package/src/contracts.ts +1 -0
- package/src/index.ts +32 -0
- package/src/modules/__tests__/assetValue.test.ts +2452 -0
- package/src/modules/__tests__/bigIntArithmetics.test.ts +410 -0
- package/src/modules/__tests__/feeMultiplier.test.ts +131 -0
- package/src/modules/__tests__/uSwapConfig.test.ts +429 -0
- package/src/modules/__tests__/uSwapNumber.test.ts +439 -0
- package/src/modules/assetValue.ts +536 -0
- package/src/modules/bigIntArithmetics.ts +366 -0
- package/src/modules/feeMultiplier.ts +84 -0
- package/src/modules/requestClient.ts +116 -0
- package/src/modules/uSwapConfig.ts +189 -0
- package/src/modules/uSwapError.ts +474 -0
- package/src/modules/uSwapNumber.ts +17 -0
- package/src/tokens.ts +1 -0
- package/src/types/commonTypes.ts +10 -0
- package/src/types/derivationPath.ts +11 -0
- package/src/types/errors/apiV1.ts +0 -0
- package/src/types/index.ts +5 -0
- package/src/types/quotes.ts +182 -0
- package/src/types/sdk.ts +38 -0
- package/src/types/wallet.ts +124 -0
- package/src/utils/__tests__/asset.test.ts +186 -0
- package/src/utils/__tests__/derivationPath.test.ts +142 -0
- package/src/utils/__tests__/explorerUrls.test.ts +59 -0
- package/src/utils/__tests__/liquidity.test.ts +302 -0
- package/src/utils/__tests__/memo.test.ts +99 -0
- package/src/utils/__tests__/others.test.ts +169 -0
- package/src/utils/__tests__/validators.test.ts +84 -0
- package/src/utils/__tests__/wallets.test.ts +625 -0
- package/src/utils/asset.ts +399 -0
- package/src/utils/chains.ts +104 -0
- package/src/utils/derivationPath.ts +101 -0
- package/src/utils/explorerUrls.ts +32 -0
- package/src/utils/liquidity.ts +154 -0
- package/src/utils/memo.ts +102 -0
- package/src/utils/others.ts +64 -0
- package/src/utils/validators.ts +36 -0
- package/src/utils/wallets.ts +238 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { Chain, CosmosChains, EVMChains, getChainConfig, UTXOChains } from "@tcswap/helpers";
|
|
3
|
+
import { getExplorerAddressUrl, getExplorerTxUrl } from "../explorerUrls";
|
|
4
|
+
|
|
5
|
+
describe("Explorer URLs", () => {
|
|
6
|
+
describe("CosmosChains", () => {
|
|
7
|
+
for (const chain of CosmosChains) {
|
|
8
|
+
test(`getExplorerTxUrl returns correct URL for ${chain}`, () => {
|
|
9
|
+
expect(getExplorerTxUrl({ chain, txHash: "0x123456789" })).toBe(
|
|
10
|
+
`${getChainConfig(chain).explorerUrl}/tx/123456789`,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect(getExplorerAddressUrl({ address: "asdfg", chain })).toBe(
|
|
14
|
+
`${getChainConfig(chain).explorerUrl}/address/asdfg`,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("EVMChains & SubstrateChains", () => {
|
|
21
|
+
for (const chain of [...EVMChains, Chain.Polkadot]) {
|
|
22
|
+
test(`getExplorerTxUrl returns correct URL for ${chain}`, () => {
|
|
23
|
+
expect(getExplorerTxUrl({ chain, txHash: "0x123456789" })).toBe(
|
|
24
|
+
`${getChainConfig(chain).explorerUrl}/tx/0x123456789`,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
expect(getExplorerAddressUrl({ address: "asdfg", chain })).toBe(
|
|
28
|
+
`${getChainConfig(chain).explorerUrl}/address/asdfg`,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test("getExplorerTxUrl adds 0x for EVM like chains", () => {
|
|
34
|
+
expect(getExplorerTxUrl({ chain: Chain.Ethereum, txHash: "12345" })).toBe("https://etherscan.io/tx/0x12345");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("UTXOChains", () => {
|
|
39
|
+
for (const chain of UTXOChains.filter((c) => c !== Chain.Dash)) {
|
|
40
|
+
test(`getExplorerTxUrl returns correct URL for ${chain}`, () => {
|
|
41
|
+
expect(getExplorerTxUrl({ chain, txHash: "0x123456789" })).toBe(
|
|
42
|
+
`${getChainConfig(chain).explorerUrl}/transaction/0x123456789`,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(getExplorerAddressUrl({ address: "asdfg", chain })).toBe(
|
|
46
|
+
`${getChainConfig(chain).explorerUrl}/address/asdfg`,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("Solana", () => {
|
|
53
|
+
test("getExplorerTxUrl returns correct URL for Solana", () => {
|
|
54
|
+
expect(getExplorerTxUrl({ chain: Chain.Solana, txHash: "b123456789" })).toBe("https://solscan.io/tx/b123456789");
|
|
55
|
+
|
|
56
|
+
expect(getExplorerAddressUrl({ address: "asdfg", chain: Chain.Solana })).toBe("https://solscan.io/account/asdfg");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getAsymmetricAssetShare,
|
|
5
|
+
getAsymmetricAssetWithdrawAmount,
|
|
6
|
+
getAsymmetricRuneShare,
|
|
7
|
+
getAsymmetricRuneWithdrawAmount,
|
|
8
|
+
getEstimatedPoolShare,
|
|
9
|
+
getLiquiditySlippage,
|
|
10
|
+
getSymmetricPoolShare,
|
|
11
|
+
getSymmetricWithdraw,
|
|
12
|
+
} from "../liquidity";
|
|
13
|
+
|
|
14
|
+
describe("getAsymmetricRuneShare", () => {
|
|
15
|
+
test("calculates rune share for typical pool position", () => {
|
|
16
|
+
const result = getAsymmetricRuneShare({
|
|
17
|
+
liquidityUnits: "100000000",
|
|
18
|
+
poolUnits: "1000000000",
|
|
19
|
+
runeDepth: "500000000000",
|
|
20
|
+
});
|
|
21
|
+
expect(result.getValue("number")).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("returns zero rune share for zero liquidity units", () => {
|
|
25
|
+
const result = getAsymmetricRuneShare({ liquidityUnits: "0", poolUnits: "1000000000", runeDepth: "500000000000" });
|
|
26
|
+
expect(result.getValue("number")).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("returns full amount when owning all pool units", () => {
|
|
30
|
+
const result = getAsymmetricRuneShare({
|
|
31
|
+
liquidityUnits: "1000000000",
|
|
32
|
+
poolUnits: "1000000000",
|
|
33
|
+
runeDepth: "500000000000",
|
|
34
|
+
});
|
|
35
|
+
expect(result.getValue("string")).toBe("5000");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("getAsymmetricAssetShare", () => {
|
|
40
|
+
test("calculates asset share for typical pool position", () => {
|
|
41
|
+
const result = getAsymmetricAssetShare({
|
|
42
|
+
assetDepth: "200000000000",
|
|
43
|
+
liquidityUnits: "100000000",
|
|
44
|
+
poolUnits: "1000000000",
|
|
45
|
+
});
|
|
46
|
+
expect(result.getValue("number")).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("returns zero asset share for zero liquidity units", () => {
|
|
50
|
+
const result = getAsymmetricAssetShare({
|
|
51
|
+
assetDepth: "200000000000",
|
|
52
|
+
liquidityUnits: "0",
|
|
53
|
+
poolUnits: "1000000000",
|
|
54
|
+
});
|
|
55
|
+
expect(result.getValue("number")).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("getAsymmetricRuneWithdrawAmount", () => {
|
|
60
|
+
test("calculates withdrawal amount for 50% withdrawal", () => {
|
|
61
|
+
const result = getAsymmetricRuneWithdrawAmount({
|
|
62
|
+
liquidityUnits: "100000000",
|
|
63
|
+
percent: 0.5,
|
|
64
|
+
poolUnits: "1000000000",
|
|
65
|
+
runeDepth: "500000000000",
|
|
66
|
+
});
|
|
67
|
+
expect(result.getValue("number")).toBeGreaterThan(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("calculates full withdrawal for 100%", () => {
|
|
71
|
+
const full = getAsymmetricRuneWithdrawAmount({
|
|
72
|
+
liquidityUnits: "100000000",
|
|
73
|
+
percent: 1,
|
|
74
|
+
poolUnits: "1000000000",
|
|
75
|
+
runeDepth: "500000000000",
|
|
76
|
+
});
|
|
77
|
+
const half = getAsymmetricRuneWithdrawAmount({
|
|
78
|
+
liquidityUnits: "100000000",
|
|
79
|
+
percent: 0.5,
|
|
80
|
+
poolUnits: "1000000000",
|
|
81
|
+
runeDepth: "500000000000",
|
|
82
|
+
});
|
|
83
|
+
expect(full.getValue("number")).toBeCloseTo(half.getValue("number") * 2, 5);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("getAsymmetricAssetWithdrawAmount", () => {
|
|
88
|
+
test("calculates withdrawal for given percentage", () => {
|
|
89
|
+
const result = getAsymmetricAssetWithdrawAmount({
|
|
90
|
+
assetDepth: "200000000000",
|
|
91
|
+
liquidityUnits: "100000000",
|
|
92
|
+
percent: 0.25,
|
|
93
|
+
poolUnits: "1000000000",
|
|
94
|
+
});
|
|
95
|
+
expect(result.getValue("number")).toBeGreaterThan(0);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("getSymmetricPoolShare", () => {
|
|
100
|
+
test("returns both rune and asset amounts", () => {
|
|
101
|
+
const result = getSymmetricPoolShare({
|
|
102
|
+
assetDepth: "200000000000",
|
|
103
|
+
liquidityUnits: "100000000",
|
|
104
|
+
poolUnits: "1000000000",
|
|
105
|
+
runeDepth: "500000000000",
|
|
106
|
+
});
|
|
107
|
+
expect(result.runeAmount.getValue("number")).toBeGreaterThan(0);
|
|
108
|
+
expect(result.assetAmount.getValue("number")).toBeGreaterThan(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("proportionally distributes based on liquidity units", () => {
|
|
112
|
+
const result10 = getSymmetricPoolShare({
|
|
113
|
+
assetDepth: "1000000000000",
|
|
114
|
+
liquidityUnits: "100000000",
|
|
115
|
+
poolUnits: "1000000000",
|
|
116
|
+
runeDepth: "1000000000000",
|
|
117
|
+
});
|
|
118
|
+
const result20 = getSymmetricPoolShare({
|
|
119
|
+
assetDepth: "1000000000000",
|
|
120
|
+
liquidityUnits: "200000000",
|
|
121
|
+
poolUnits: "1000000000",
|
|
122
|
+
runeDepth: "1000000000000",
|
|
123
|
+
});
|
|
124
|
+
expect(result20.runeAmount.getValue("number")).toBeCloseTo(result10.runeAmount.getValue("number") * 2, 5);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("getSymmetricWithdraw", () => {
|
|
129
|
+
test("applies percentage to both amounts", () => {
|
|
130
|
+
const result = getSymmetricWithdraw({
|
|
131
|
+
assetDepth: "200000000000",
|
|
132
|
+
liquidityUnits: "100000000",
|
|
133
|
+
percent: 0.5,
|
|
134
|
+
poolUnits: "1000000000",
|
|
135
|
+
runeDepth: "500000000000",
|
|
136
|
+
});
|
|
137
|
+
expect(result.runeAmount?.getValue("number")).toBeGreaterThan(0);
|
|
138
|
+
expect(result.assetAmount?.getValue("number")).toBeGreaterThan(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("full withdrawal equals pool share", () => {
|
|
142
|
+
const params = {
|
|
143
|
+
assetDepth: "200000000000",
|
|
144
|
+
liquidityUnits: "100000000",
|
|
145
|
+
poolUnits: "1000000000",
|
|
146
|
+
runeDepth: "500000000000",
|
|
147
|
+
};
|
|
148
|
+
const share = getSymmetricPoolShare(params);
|
|
149
|
+
const withdraw = getSymmetricWithdraw({ ...params, percent: 1 });
|
|
150
|
+
expect(withdraw.runeAmount?.getValue("string")).toBe(share.runeAmount.getValue("string"));
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("getEstimatedPoolShare", () => {
|
|
155
|
+
test("estimates pool share after adding liquidity", () => {
|
|
156
|
+
// Returns base value representation (with 8 decimals internally)
|
|
157
|
+
// For ~4.76% share, base value is 4761905 (0.04761905 * 10^8)
|
|
158
|
+
const result = getEstimatedPoolShare({
|
|
159
|
+
assetAmount: "10000000000",
|
|
160
|
+
assetDepth: "200000000000",
|
|
161
|
+
liquidityUnits: "0",
|
|
162
|
+
poolUnits: "1000000000",
|
|
163
|
+
runeAmount: "25000000000",
|
|
164
|
+
runeDepth: "500000000000",
|
|
165
|
+
});
|
|
166
|
+
expect(result).toBeGreaterThan(0);
|
|
167
|
+
// Base value for percentage < 100% is < 10^8
|
|
168
|
+
expect(result).toBeLessThan(100_000_000);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("returns 0 when adding zero liquidity", () => {
|
|
172
|
+
const result = getEstimatedPoolShare({
|
|
173
|
+
assetAmount: "0",
|
|
174
|
+
assetDepth: "200000000000",
|
|
175
|
+
liquidityUnits: "0",
|
|
176
|
+
poolUnits: "1000000000",
|
|
177
|
+
runeAmount: "0",
|
|
178
|
+
runeDepth: "500000000000",
|
|
179
|
+
});
|
|
180
|
+
expect(result).toBe(0);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("getLiquiditySlippage", () => {
|
|
185
|
+
test("returns 0 when any amount is zero", () => {
|
|
186
|
+
expect(
|
|
187
|
+
getLiquiditySlippage({ assetAmount: "0", assetDepth: "200000000000", runeAmount: "100", runeDepth: "500" }),
|
|
188
|
+
).toBe(0);
|
|
189
|
+
expect(getLiquiditySlippage({ assetAmount: "100", assetDepth: "0", runeAmount: "100", runeDepth: "500" })).toBe(0);
|
|
190
|
+
expect(getLiquiditySlippage({ assetAmount: "100", assetDepth: "200", runeAmount: "0", runeDepth: "500" })).toBe(0);
|
|
191
|
+
expect(getLiquiditySlippage({ assetAmount: "100", assetDepth: "200", runeAmount: "100", runeDepth: "0" })).toBe(0);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("calculates slippage for balanced add", () => {
|
|
195
|
+
const result = getLiquiditySlippage({
|
|
196
|
+
assetAmount: "100000000",
|
|
197
|
+
assetDepth: "200000000000",
|
|
198
|
+
runeAmount: "250000000",
|
|
199
|
+
runeDepth: "500000000000",
|
|
200
|
+
});
|
|
201
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
202
|
+
expect(result).toBeLessThan(1);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("returns absolute value (no negatives)", () => {
|
|
206
|
+
const result = getLiquiditySlippage({
|
|
207
|
+
assetAmount: "1000000000",
|
|
208
|
+
assetDepth: "200000000000",
|
|
209
|
+
runeAmount: "100000000",
|
|
210
|
+
runeDepth: "500000000000",
|
|
211
|
+
});
|
|
212
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("edge cases", () => {
|
|
217
|
+
test("handles very large numbers without overflow", () => {
|
|
218
|
+
const result = getAsymmetricRuneShare({
|
|
219
|
+
liquidityUnits: "999999999999999999",
|
|
220
|
+
poolUnits: "9999999999999999999",
|
|
221
|
+
runeDepth: "99999999999999999999",
|
|
222
|
+
});
|
|
223
|
+
expect(result.getValue("number")).toBeGreaterThan(0);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("getSymmetricPoolShare with equal depths", () => {
|
|
227
|
+
const result = getSymmetricPoolShare({
|
|
228
|
+
assetDepth: "1000000000000",
|
|
229
|
+
liquidityUnits: "500000000",
|
|
230
|
+
poolUnits: "1000000000",
|
|
231
|
+
runeDepth: "1000000000000",
|
|
232
|
+
});
|
|
233
|
+
// 50% of pool should get 50% of both depths
|
|
234
|
+
expect(result.runeAmount.getValue("string")).toBe(result.assetAmount.getValue("string"));
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("getEstimatedPoolShare with existing liquidity units", () => {
|
|
238
|
+
const result = getEstimatedPoolShare({
|
|
239
|
+
assetAmount: "10000000000",
|
|
240
|
+
assetDepth: "200000000000",
|
|
241
|
+
liquidityUnits: "50000000", // User already has some units
|
|
242
|
+
poolUnits: "1000000000",
|
|
243
|
+
runeAmount: "25000000000",
|
|
244
|
+
runeDepth: "500000000000",
|
|
245
|
+
});
|
|
246
|
+
expect(result).toBeGreaterThan(0);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("handles single-sided liquidity add (only rune)", () => {
|
|
250
|
+
const result = getEstimatedPoolShare({
|
|
251
|
+
assetAmount: "0",
|
|
252
|
+
assetDepth: "200000000000",
|
|
253
|
+
liquidityUnits: "0",
|
|
254
|
+
poolUnits: "1000000000",
|
|
255
|
+
runeAmount: "25000000000",
|
|
256
|
+
runeDepth: "500000000000",
|
|
257
|
+
});
|
|
258
|
+
expect(result).toBeGreaterThan(0);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test("handles single-sided liquidity add (only asset)", () => {
|
|
262
|
+
const result = getEstimatedPoolShare({
|
|
263
|
+
assetAmount: "10000000000",
|
|
264
|
+
assetDepth: "200000000000",
|
|
265
|
+
liquidityUnits: "0",
|
|
266
|
+
poolUnits: "1000000000",
|
|
267
|
+
runeAmount: "0",
|
|
268
|
+
runeDepth: "500000000000",
|
|
269
|
+
});
|
|
270
|
+
expect(result).toBeGreaterThan(0);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("getAsymmetricRuneWithdrawAmount with 0% withdrawal returns 0", () => {
|
|
274
|
+
const result = getAsymmetricRuneWithdrawAmount({
|
|
275
|
+
liquidityUnits: "100000000",
|
|
276
|
+
percent: 0,
|
|
277
|
+
poolUnits: "1000000000",
|
|
278
|
+
runeDepth: "500000000000",
|
|
279
|
+
});
|
|
280
|
+
expect(result.getValue("number")).toBe(0);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("getLiquiditySlippage higher slippage for unbalanced add", () => {
|
|
284
|
+
// Balanced add (proportional to pool)
|
|
285
|
+
const balanced = getLiquiditySlippage({
|
|
286
|
+
assetAmount: "100000000",
|
|
287
|
+
assetDepth: "200000000000",
|
|
288
|
+
runeAmount: "250000000",
|
|
289
|
+
runeDepth: "500000000000",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Unbalanced add (too much rune)
|
|
293
|
+
const unbalanced = getLiquiditySlippage({
|
|
294
|
+
assetAmount: "100000000",
|
|
295
|
+
assetDepth: "200000000000",
|
|
296
|
+
runeAmount: "1000000000", // Much higher rune
|
|
297
|
+
runeDepth: "500000000000",
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(unbalanced).toBeGreaterThan(balanced);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { Chain } from "@tcswap/types";
|
|
3
|
+
import { MemoType } from "../../types";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getMemoForDeposit,
|
|
7
|
+
getMemoForLeaveAndBond,
|
|
8
|
+
getMemoForNamePreferredAssetRegister,
|
|
9
|
+
getMemoForNameRegister,
|
|
10
|
+
getMemoForRunePoolDeposit,
|
|
11
|
+
getMemoForRunePoolWithdraw,
|
|
12
|
+
getMemoForWithdraw,
|
|
13
|
+
} from "../memo";
|
|
14
|
+
|
|
15
|
+
describe("getMemoForLeaveAndBond", () => {
|
|
16
|
+
test("returns correct memo for Leave", () => {
|
|
17
|
+
const result = getMemoForLeaveAndBond({ address: "ABC123", type: MemoType.LEAVE });
|
|
18
|
+
expect(result).toBe("LEAVE:ABC123");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("returns correct memo for Bond", () => {
|
|
22
|
+
const result = getMemoForLeaveAndBond({ address: "ABC123", type: MemoType.BOND });
|
|
23
|
+
expect(result).toBe("BOND:ABC123");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("getMemoForNameRegister", () => {
|
|
28
|
+
test("returns correct memo for name registration", () => {
|
|
29
|
+
const result = getMemoForNameRegister({
|
|
30
|
+
address: "0xaasd123",
|
|
31
|
+
chain: Chain.Ethereum,
|
|
32
|
+
name: "asdfg",
|
|
33
|
+
owner: "thor1234",
|
|
34
|
+
});
|
|
35
|
+
expect(result).toBe("~:asdfg:ETH:0xaasd123:thor1234");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("getMemoForNamePreferredAssetRegister", () => {
|
|
40
|
+
test("returns correct memo for preferred asset registration", () => {
|
|
41
|
+
const result = getMemoForNamePreferredAssetRegister({
|
|
42
|
+
asset: "ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48",
|
|
43
|
+
chain: Chain.Ethereum,
|
|
44
|
+
name: "asdfg",
|
|
45
|
+
owner: "thor1234",
|
|
46
|
+
payout: "0x6621d872f17109d6601c49edba526ebcfd332d5d",
|
|
47
|
+
});
|
|
48
|
+
expect(result).toBe(
|
|
49
|
+
"~:asdfg:ETH:0x6621d872f17109d6601c49edba526ebcfd332d5d:thor1234:ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48",
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("getMemoForDeposit", () => {
|
|
55
|
+
test("returns correct memo for deposit", () => {
|
|
56
|
+
const result = getMemoForDeposit({ chain: Chain.Ethereum, symbol: "ETH" });
|
|
57
|
+
expect(result).toBe("+:ETH.ETH");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("returns correct memo when paired address is not available but affiliate info is present", () => {
|
|
61
|
+
const result = getMemoForDeposit({
|
|
62
|
+
affiliateAddress: "thor1abc123",
|
|
63
|
+
affiliateBasisPoints: 500,
|
|
64
|
+
chain: Chain.Ethereum,
|
|
65
|
+
symbol: "ETH",
|
|
66
|
+
});
|
|
67
|
+
expect(result).toBe("+:ETH.ETH::thor1abc123:500");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("getMemoForWithdraw", () => {
|
|
72
|
+
test("returns correct memo for withdraw", () => {
|
|
73
|
+
const result = getMemoForWithdraw({ basisPoints: 100, chain: Chain.Ethereum, symbol: "ETH", ticker: "ETH" });
|
|
74
|
+
expect(result).toBe("-:ETH.ETH:100");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("getMemoForRunePoolDeposit", () => {
|
|
79
|
+
test("returns correct memo for rune pool deposit", () => {
|
|
80
|
+
const result = getMemoForRunePoolDeposit();
|
|
81
|
+
expect(result).toBe("POOL+");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("getMemoForRunePoolWithdraw", () => {
|
|
86
|
+
test("returns correct memo for rune pool withdraw", () => {
|
|
87
|
+
const result = getMemoForRunePoolWithdraw({ basisPoints: 500 });
|
|
88
|
+
expect(result).toBe("POOL-:500");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("returns correct memo when affiliate info is present", () => {
|
|
92
|
+
const result = getMemoForRunePoolWithdraw({
|
|
93
|
+
affiliateAddress: "thor1abc123",
|
|
94
|
+
affiliateBasisPoints: 500,
|
|
95
|
+
basisPoints: 500,
|
|
96
|
+
});
|
|
97
|
+
expect(result).toBe("POOL-:500:thor1abc123:500");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { Chain } from "@tcswap/types";
|
|
7
|
+
|
|
8
|
+
import { findAssetBy } from "../asset";
|
|
9
|
+
import { getChainIdentifier, getMAYANameCost, getTHORNameCost, warnOnce, wrapWithThrow } from "../others";
|
|
10
|
+
|
|
11
|
+
describe("getTHORNameCost", () => {
|
|
12
|
+
test("returns 10 + numberOfYears", () => {
|
|
13
|
+
expect(getTHORNameCost(0)).toBe(10);
|
|
14
|
+
expect(getTHORNameCost(1)).toBe(11);
|
|
15
|
+
expect(getTHORNameCost(5)).toBe(15);
|
|
16
|
+
expect(getTHORNameCost(10)).toBe(20);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("throws for negative years in THORName cost", () => {
|
|
20
|
+
expect(() => getTHORNameCost(-1)).toThrow("helpers_invalid_number_of_years");
|
|
21
|
+
expect(() => getTHORNameCost(-100)).toThrow("helpers_invalid_number_of_years");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("getMAYANameCost", () => {
|
|
26
|
+
test("returns 10 + (numberOfYears * 1.0512)", () => {
|
|
27
|
+
expect(getMAYANameCost(0)).toBe(10);
|
|
28
|
+
expect(getMAYANameCost(1)).toBe(11.0512);
|
|
29
|
+
expect(getMAYANameCost(5)).toBeCloseTo(15.256, 3);
|
|
30
|
+
expect(getMAYANameCost(10)).toBeCloseTo(20.512, 3);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("throws for negative years in MAYAName cost", () => {
|
|
34
|
+
expect(() => getMAYANameCost(-1)).toThrow("helpers_invalid_number_of_years");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("getChainIdentifier", () => {
|
|
39
|
+
test("returns CHAIN.RUNE for THORChain", () => {
|
|
40
|
+
expect(getChainIdentifier(Chain.THORChain)).toBe("THOR.RUNE");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("returns CHAIN.ATOM for Cosmos", () => {
|
|
44
|
+
expect(getChainIdentifier(Chain.Cosmos)).toBe("GAIA.ATOM");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("returns CHAIN only for BinanceSmartChain", () => {
|
|
48
|
+
expect(getChainIdentifier(Chain.BinanceSmartChain)).toBe("BSC");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("returns CHAIN.CHAIN for other chains", () => {
|
|
52
|
+
expect(getChainIdentifier(Chain.Bitcoin)).toBe("BTC.BTC");
|
|
53
|
+
expect(getChainIdentifier(Chain.Ethereum)).toBe("ETH.ETH");
|
|
54
|
+
expect(getChainIdentifier(Chain.Avalanche)).toBe("AVAX.AVAX");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("wrapWithThrow", () => {
|
|
59
|
+
test("returns function result on success", () => {
|
|
60
|
+
expect(wrapWithThrow(() => 42)).toBe(42);
|
|
61
|
+
expect(wrapWithThrow(() => "hello")).toBe("hello");
|
|
62
|
+
expect(wrapWithThrow(() => ({ a: 1 }))).toEqual({ a: 1 });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("returns undefined on error without errorKey", () => {
|
|
66
|
+
expect(
|
|
67
|
+
wrapWithThrow(() => {
|
|
68
|
+
throw new Error("test");
|
|
69
|
+
}),
|
|
70
|
+
).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("throws USwapError when errorKey provided", () => {
|
|
74
|
+
expect(() =>
|
|
75
|
+
wrapWithThrow(() => {
|
|
76
|
+
throw new Error("test");
|
|
77
|
+
}, "helpers_invalid_identifier"),
|
|
78
|
+
).toThrow("helpers_invalid_identifier");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("warnOnce", () => {
|
|
83
|
+
// Note: warnOnce skips console.warn in test environment (NODE_ENV=test)
|
|
84
|
+
// We test the behavior by calling the function multiple times and observing
|
|
85
|
+
// that the function doesn't throw and handles repeated calls gracefully
|
|
86
|
+
|
|
87
|
+
test("does not throw when condition is false", () => {
|
|
88
|
+
expect(() => warnOnce({ condition: false, id: "test_false_condition", warning: "should not warn" })).not.toThrow();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("does not throw when condition is true", () => {
|
|
92
|
+
const uniqueId = `test_warns_${Date.now()}`;
|
|
93
|
+
expect(() => warnOnce({ condition: true, id: uniqueId, warning: "test warning message" })).not.toThrow();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("handles repeated calls with same id without throwing", () => {
|
|
97
|
+
const uniqueId = `test_once_${Date.now()}`;
|
|
98
|
+
expect(() => {
|
|
99
|
+
warnOnce({ condition: true, id: uniqueId, warning: "first warn" });
|
|
100
|
+
warnOnce({ condition: true, id: uniqueId, warning: "second warn" });
|
|
101
|
+
warnOnce({ condition: true, id: uniqueId, warning: "third warn" });
|
|
102
|
+
}).not.toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("handles multiple different ids without throwing", () => {
|
|
106
|
+
const uniqueId1 = `test_different_1_${Date.now()}`;
|
|
107
|
+
const uniqueId2 = `test_different_2_${Date.now()}`;
|
|
108
|
+
expect(() => {
|
|
109
|
+
warnOnce({ condition: true, id: uniqueId1, warning: "warn 1" });
|
|
110
|
+
warnOnce({ condition: true, id: uniqueId2, warning: "warn 2" });
|
|
111
|
+
}).not.toThrow();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("getAssetBy", () => {
|
|
116
|
+
test("find ETH asset by identifier", async () => {
|
|
117
|
+
const assetByIdentifier = await findAssetBy({ identifier: "ETH.ETH" });
|
|
118
|
+
expect(assetByIdentifier).toBe("ETH.ETH");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("find ETH token by chain and contract", async () => {
|
|
122
|
+
const assetByChainAndContract = await findAssetBy({
|
|
123
|
+
chain: Chain.Ethereum,
|
|
124
|
+
contract: "0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48",
|
|
125
|
+
});
|
|
126
|
+
expect(assetByChainAndContract?.toUpperCase()).toBe("ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("return undefined if asset can't be found", async () => {
|
|
130
|
+
const assetByIdentifier = await findAssetBy({ identifier: "ARB.NOTEXISTINGTOKEN" });
|
|
131
|
+
const assetByChainAndContract = await findAssetBy({ chain: Chain.Ethereum, contract: "NOTFOUND" });
|
|
132
|
+
expect(assetByIdentifier).toBeUndefined();
|
|
133
|
+
expect(assetByChainAndContract).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe(Chain.Radix, () => {
|
|
137
|
+
test("find Radix XRD by identifier", async () => {
|
|
138
|
+
const assetByChainAndContract = await findAssetBy({ identifier: "XRD.XRD" });
|
|
139
|
+
expect(assetByChainAndContract?.toUpperCase()).toBe("XRD.XRD".toUpperCase());
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("find Radix token by chain and contract", async () => {
|
|
143
|
+
const assetByChainAndContract = await findAssetBy({
|
|
144
|
+
chain: Chain.Radix,
|
|
145
|
+
contract: "resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75",
|
|
146
|
+
});
|
|
147
|
+
expect(assetByChainAndContract?.toUpperCase()).toBe(
|
|
148
|
+
"XRD.XWBTC-resource_rdx1t580qxc7upat7lww4l2c4jckacafjeudxj5wpjrrct0p3e82sq4y75".toUpperCase(),
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe(Chain.Solana, () => {
|
|
154
|
+
test("find Solana SOL by identifier", async () => {
|
|
155
|
+
const assetByChainAndContract = await findAssetBy({ identifier: "SOL.SOL" });
|
|
156
|
+
expect(assetByChainAndContract?.toUpperCase()).toBe("SOL.SOL".toUpperCase());
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("find Solana token by chain and contract", async () => {
|
|
160
|
+
const assetByChainAndContract = await findAssetBy({
|
|
161
|
+
chain: Chain.Solana,
|
|
162
|
+
contract: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
163
|
+
});
|
|
164
|
+
expect(assetByChainAndContract?.toUpperCase()).toBe(
|
|
165
|
+
"SOL.USDC-EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".toUpperCase(),
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|