@swapkit/helpers 1.0.0-rc.0

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.
@@ -0,0 +1,157 @@
1
+ import { BaseDecimal, Chain } from '@swapkit/types';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { getAssetType, getDecimal } from '../asset.ts';
5
+
6
+ const tickerMap: Record<string, string> = {
7
+ [Chain.THORChain]: 'RUNE',
8
+ [Chain.Cosmos]: 'ATOM',
9
+ [Chain.BinanceSmartChain]: 'BNB',
10
+ };
11
+
12
+ describe('getAssetType', () => {
13
+ describe('when isSynth is true', () => {
14
+ it('should return "Synth"', () => {
15
+ const result = getAssetType({ chain: Chain.Bitcoin, symbol: 'BTC/BTC' });
16
+ expect(result).toBe('Synth');
17
+ });
18
+ });
19
+
20
+ describe('when isSynth is false', () => {
21
+ describe('for native chains and their assets', () => {
22
+ Object.values(Chain).forEach((chain) => {
23
+ it(`should return "Native" for chain ${chain} asset`, () => {
24
+ const ticker = tickerMap[chain] || chain;
25
+ const result = getAssetType({ chain: chain as Chain, symbol: ticker });
26
+
27
+ expect(result).toBe('Native');
28
+ });
29
+ });
30
+ });
31
+
32
+ describe('for Cosmos chain', () => {
33
+ it('should return "GAIA" for non-ATOM tickers', () => {
34
+ const result = getAssetType({ chain: Chain.Cosmos, symbol: 'NOT_ATOM' });
35
+ expect(result).toBe('GAIA');
36
+ });
37
+ });
38
+
39
+ describe('for Binance chain', () => {
40
+ it('should return "BEP2" for non-BNB tickers', () => {
41
+ const result = getAssetType({ chain: Chain.Binance, symbol: 'NOT_BNB' });
42
+ expect(result).toBe('BEP2');
43
+ });
44
+ });
45
+
46
+ describe('for Binance Smart Chain', () => {
47
+ it('should return "BEP20" for non-BNB tickers', () => {
48
+ const result = getAssetType({ chain: Chain.BinanceSmartChain, symbol: 'NOT_BNB' });
49
+ expect(result).toBe('BEP20');
50
+ });
51
+ });
52
+
53
+ describe('for Ethereum chain', () => {
54
+ it('should return "ERC20" for non-ETH tickers', () => {
55
+ const result = getAssetType({ chain: Chain.Ethereum, symbol: 'NOT_ETH' });
56
+ expect(result).toBe('ERC20');
57
+ });
58
+ });
59
+
60
+ describe('for Avalanche chain', () => {
61
+ it('should return "AVAX" for non-AVAX tickers', () => {
62
+ const result = getAssetType({ chain: Chain.Avalanche, symbol: 'NOT_AVAX' });
63
+ expect(result).toBe('AVAX');
64
+ });
65
+ });
66
+ });
67
+ });
68
+
69
+ describe('getDecimal', () => {
70
+ /**
71
+ * Test out native
72
+ */
73
+ Object.values(Chain)
74
+ .filter((c) => ![Chain.Ethereum, Chain.Avalanche].includes(c))
75
+ .forEach((chain) => {
76
+ describe(chain, () => {
77
+ it(`returns proper decimal for native ${chain} asset`, async () => {
78
+ const decimal = await getDecimal({ chain, symbol: chain });
79
+ expect(decimal).toBe(BaseDecimal[chain]);
80
+ });
81
+ });
82
+ });
83
+
84
+ describe('ETH', () => {
85
+ it("returns proper decimal for eth and it's assets", async () => {
86
+ const ethDecimal = await getDecimal({ chain: Chain.Ethereum, symbol: 'ETH' });
87
+ expect(ethDecimal).toBe(BaseDecimal.ETH);
88
+
89
+ const usdcDecimal = await getDecimal({
90
+ chain: Chain.Ethereum,
91
+ symbol: 'USDC-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
92
+ });
93
+ expect(usdcDecimal).toBe(6);
94
+
95
+ const wbtcDecimal = await getDecimal({
96
+ chain: Chain.Ethereum,
97
+ symbol: 'WBTC-0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
98
+ });
99
+ expect(wbtcDecimal).toBe(8);
100
+
101
+ const decDecimal = await getDecimal({
102
+ chain: Chain.Ethereum,
103
+ symbol: 'ZIL-0x05f4a42e251f2d52b8ed15e9fedaacfcef1fad27',
104
+ });
105
+ expect(decDecimal).toBe(12);
106
+
107
+ const kindDecimal = await getDecimal({
108
+ chain: Chain.Ethereum,
109
+ symbol: 'KIND-0x4618519de4c304f3444ffa7f812dddc2971cc688',
110
+ });
111
+ expect(kindDecimal).toBe(8);
112
+
113
+ const shitcoinDecimal = await getDecimal({
114
+ chain: Chain.Ethereum,
115
+ symbol: 'HOMI-0xCa208BfD69ae6D2667f1FCbE681BAe12767c0078',
116
+ });
117
+ expect(shitcoinDecimal).toBe(0);
118
+ });
119
+ });
120
+
121
+ describe('AVAX', () => {
122
+ it("returns proper decimal for avax and it's assets", async () => {
123
+ const avaxDecimal = await getDecimal({ chain: Chain.Avalanche, symbol: 'AVAX' });
124
+ expect(avaxDecimal).toBe(BaseDecimal.AVAX);
125
+
126
+ const wbtceDecimal = await getDecimal({
127
+ chain: Chain.Avalanche,
128
+ symbol: 'WBTC.e-0x50b7545627a5162f82a992c33b87adc75187b218',
129
+ });
130
+ expect(wbtceDecimal).toBe(8);
131
+
132
+ const btcbDecimal = await getDecimal({
133
+ chain: Chain.Avalanche,
134
+ symbol: 'BTC.b-0x152b9d0FdC40C096757F570A51E494bd4b943E50',
135
+ });
136
+ expect(btcbDecimal).toBe(8);
137
+
138
+ const timeDecimal = await getDecimal({
139
+ chain: Chain.Avalanche,
140
+ symbol: 'TIME-0xb54f16fB19478766A268F172C9480f8da1a7c9C3',
141
+ });
142
+ expect(timeDecimal).toBe(9);
143
+
144
+ const usdtDecimal = await getDecimal({
145
+ chain: Chain.Avalanche,
146
+ symbol: 'USDT-0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7',
147
+ });
148
+ expect(usdtDecimal).toBe(6);
149
+
150
+ const usdcDecimal = await getDecimal({
151
+ chain: Chain.Avalanche,
152
+ symbol: 'USDC-0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
153
+ });
154
+ expect(usdcDecimal).toBe(6);
155
+ });
156
+ });
157
+ });
@@ -0,0 +1,79 @@
1
+ import { Chain, MemoType } from '@swapkit/types';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { getMemoFor } from '../memo.ts';
5
+
6
+ describe('getMemoFor', () => {
7
+ describe('for Leave, Upgrade, and Bond', () => {
8
+ [
9
+ [MemoType.LEAVE, 'LEAVE:ABC123'],
10
+ [MemoType.BOND, 'BOND:ABC123'],
11
+ ].forEach(([memoType, expected]) => {
12
+ it(`returns correct memo for ${memoType}`, () => {
13
+ const result = getMemoFor(memoType as MemoType, { address: 'ABC123' });
14
+ expect(result).toBe(expected);
15
+ });
16
+ });
17
+ });
18
+
19
+ describe('for Unbond and Thorname Register', () => {
20
+ it('returns correct memo for Unbond', () => {
21
+ const result = getMemoFor(MemoType.UNBOND, { address: 'ABC123', unbondAmount: 10 });
22
+ expect(result).toBe('UNBOND:ABC123:1000000000');
23
+ });
24
+
25
+ it('returns correct memo for Thorname Register', () => {
26
+ const result = getMemoFor(MemoType.THORNAME_REGISTER, {
27
+ name: 'thorname',
28
+ chain: 'BNB',
29
+ address: '0xABC123',
30
+ owner: '0xDEF456',
31
+ });
32
+ expect(result).toBe('~:thorname:BNB:0xABC123:0xDEF456');
33
+ });
34
+ });
35
+
36
+ describe('for Deposit', () => {
37
+ it('returns correct memo for Deposit (single side)', () => {
38
+ const result = getMemoFor(MemoType.DEPOSIT, {
39
+ chain: Chain.Ethereum,
40
+ symbol: 'ETH',
41
+ singleSide: true,
42
+ });
43
+ expect(result).toBe('+:ETH/ETH::t:0');
44
+ });
45
+
46
+ it('returns correct memo for Deposit (dual side)', () => {
47
+ const result = getMemoFor(MemoType.DEPOSIT, {
48
+ chain: Chain.Avalanche,
49
+ symbol: 'AVAX',
50
+ address: '0xABC123',
51
+ });
52
+ expect(result).toBe('+:AVAX.AVAX:0xABC123:t:0');
53
+ });
54
+ });
55
+
56
+ describe('for Withdraw', () => {
57
+ it('returns correct memo for Withdraw (single side)', () => {
58
+ const result = getMemoFor(MemoType.WITHDRAW, {
59
+ chain: Chain.Bitcoin,
60
+ ticker: 'BTC',
61
+ symbol: 'BTC',
62
+ basisPoints: 10000,
63
+ singleSide: true,
64
+ });
65
+ expect(result).toBe('-:BTC/BTC:10000');
66
+ });
67
+
68
+ it('returns correct memo for Withdraw (dual side)', () => {
69
+ const result = getMemoFor(MemoType.WITHDRAW, {
70
+ chain: Chain.Ethereum,
71
+ ticker: 'ETH',
72
+ symbol: 'ETH',
73
+ basisPoints: 100,
74
+ targetAssetString: 'ETH.ETH',
75
+ });
76
+ expect(result).toBe('-:ETH.ETH:100:ETH.ETH');
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { derivationPathToString, getTHORNameCost, validateTHORName } from '../others.ts';
4
+
5
+ describe('derivationPathToString', () => {
6
+ it('should return the correct string for a full path', () => {
7
+ const path = [1, 2, 3, 4, 5];
8
+ const result = derivationPathToString(path);
9
+ expect(result).toEqual("1'/2'/3'/4/5");
10
+ });
11
+
12
+ it('should return the correct string for a short path', () => {
13
+ const path = [1, 2, 3, 4];
14
+ const result = derivationPathToString(path);
15
+ expect(result).toEqual("1'/2'/3'/4");
16
+ });
17
+ });
18
+
19
+ describe('getTHORNameCost', () => {
20
+ describe('for correct values', () => {
21
+ [
22
+ [1, 11],
23
+ [2, 12],
24
+ [3, 13],
25
+ [10, 20],
26
+ ].forEach(([years, expected]) => {
27
+ it(`returns correct ${expected} cost for ${years} years`, () => {
28
+ const result = getTHORNameCost(years);
29
+ expect(result).toBe(expected);
30
+ });
31
+ });
32
+ });
33
+
34
+ it('throws an error for negative years', () => {
35
+ expect(() => getTHORNameCost(-1)).toThrowError('Invalid number of year');
36
+ });
37
+ });
38
+
39
+ describe('validateTHORName', () => {
40
+ const casesWithExpectation: [string, boolean][] = [
41
+ ['validname', true],
42
+ ['valid-name', true],
43
+ ['valid_name', true],
44
+ ['valid+name', true],
45
+ ['name_with_numbers123', true],
46
+ ['UPPER_CASE', true],
47
+ ['toolongname123456789012345678901', false],
48
+ ['invalid@name', false],
49
+ ['invalid!name', false],
50
+ ['invalid#name', false],
51
+ ];
52
+
53
+ casesWithExpectation.forEach(([name, expected]) => {
54
+ it(`returns ${expected} for THORName "${name}"`, () => {
55
+ const result = validateTHORName(name);
56
+ expect(result).toBe(expected);
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,177 @@
1
+ import type { EVMChain } from '@swapkit/types';
2
+ import { BaseDecimal, Chain, ChainToRPC, FeeOption } from '@swapkit/types';
3
+
4
+ import { postRequest } from './others.ts';
5
+
6
+ const getDecimalMethodHex = '0x313ce567';
7
+
8
+ export type CommonAssetString = 'MAYA.MAYA' | 'ETH.THOR' | 'ETH.vTHOR' | Chain;
9
+
10
+ const getContractDecimals = async ({ chain, to }: { chain: EVMChain; to: string }) => {
11
+ try {
12
+ const response = await postRequest<string>(
13
+ ChainToRPC[chain],
14
+ JSON.stringify({
15
+ method: 'eth_call',
16
+ params: [{ to: to.toLowerCase(), data: getDecimalMethodHex }, 'latest'],
17
+ id: 44,
18
+ jsonrpc: '2.0',
19
+ }),
20
+ { accept: '*/*', 'cache-control': 'no-cache', 'content-type': 'application/json' },
21
+ true,
22
+ );
23
+
24
+ const { result } = JSON.parse(response) as { result: string };
25
+
26
+ return parseInt(BigInt(result).toString());
27
+ } catch (error) {
28
+ console.error(error);
29
+ return BaseDecimal[chain];
30
+ }
31
+ };
32
+
33
+ const getETHAssetDecimal = async (symbol: string) => {
34
+ if (symbol === Chain.Ethereum) return BaseDecimal.ETH;
35
+ const [, address] = symbol.split('-');
36
+
37
+ return address?.startsWith('0x')
38
+ ? getContractDecimals({ chain: Chain.Ethereum, to: address })
39
+ : BaseDecimal.ETH;
40
+ };
41
+
42
+ const getAVAXAssetDecimal = async (symbol: string) => {
43
+ const [, address] = symbol.split('-');
44
+
45
+ return address?.startsWith('0x')
46
+ ? getContractDecimals({ chain: Chain.Avalanche, to: address.toLowerCase() })
47
+ : BaseDecimal.AVAX;
48
+ };
49
+
50
+ const getBSCAssetDecimal = async (symbol: string) => {
51
+ if (symbol === Chain.BinanceSmartChain) return BaseDecimal.BSC;
52
+
53
+ return BaseDecimal.BSC;
54
+ };
55
+
56
+ export const getDecimal = async ({ chain, symbol }: { chain: Chain; symbol: string }) => {
57
+ switch (chain) {
58
+ case Chain.Ethereum:
59
+ return getETHAssetDecimal(symbol);
60
+ case Chain.Avalanche:
61
+ return getAVAXAssetDecimal(symbol);
62
+ case Chain.BinanceSmartChain:
63
+ return getBSCAssetDecimal(symbol);
64
+ default:
65
+ return BaseDecimal[chain];
66
+ }
67
+ };
68
+
69
+ export const gasFeeMultiplier: Record<FeeOption, number> = {
70
+ [FeeOption.Average]: 1.2,
71
+ [FeeOption.Fast]: 1.5,
72
+ [FeeOption.Fastest]: 2,
73
+ };
74
+
75
+ export const isGasAsset = ({ chain, symbol }: { chain: Chain; symbol: string }) => {
76
+ switch (chain) {
77
+ case Chain.Bitcoin:
78
+ case Chain.BitcoinCash:
79
+ case Chain.Litecoin:
80
+ case Chain.Dogecoin:
81
+ case Chain.Binance:
82
+ case Chain.Ethereum:
83
+ case Chain.Avalanche:
84
+ return symbol === chain;
85
+
86
+ case Chain.Arbitrum:
87
+ case Chain.Optimism:
88
+ return 'ETH' === symbol;
89
+
90
+ case Chain.Maya:
91
+ return symbol === 'CACAO';
92
+
93
+ case Chain.Cosmos:
94
+ return symbol === 'ATOM';
95
+ case Chain.Polygon:
96
+ return symbol === 'MATIC';
97
+ case Chain.BinanceSmartChain:
98
+ return symbol === 'BNB';
99
+ case Chain.THORChain:
100
+ return symbol === 'RUNE';
101
+ }
102
+ };
103
+
104
+ export const getCommonAssetInfo = (
105
+ assetString: CommonAssetString,
106
+ ): { identifier: string; decimal: number } => {
107
+ switch (assetString) {
108
+ case 'ETH.THOR':
109
+ return { identifier: 'ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044', decimal: 18 };
110
+ case 'ETH.vTHOR':
111
+ return { identifier: 'ETH.vTHOR-0x815c23eca83261b6ec689b60cc4a58b54bc24d8d', decimal: 18 };
112
+
113
+ case Chain.Cosmos:
114
+ return { identifier: 'GAIA.ATOM', decimal: BaseDecimal[assetString] };
115
+ case Chain.THORChain:
116
+ return { identifier: 'THOR.RUNE', decimal: BaseDecimal[assetString] };
117
+ case Chain.BinanceSmartChain:
118
+ return { identifier: 'BSC.BNB', decimal: BaseDecimal[assetString] };
119
+ case Chain.Maya:
120
+ return { identifier: 'MAYA.CACAO', decimal: BaseDecimal.MAYA };
121
+ case 'MAYA.MAYA':
122
+ return { identifier: 'MAYA.MAYA', decimal: 4 };
123
+
124
+ case Chain.Arbitrum:
125
+ case Chain.Optimism:
126
+ case Chain.BitcoinCash:
127
+ case Chain.Litecoin:
128
+ case Chain.Dogecoin:
129
+ case Chain.Binance:
130
+ case Chain.Avalanche:
131
+ case Chain.Polygon:
132
+ case Chain.Bitcoin:
133
+ case Chain.Ethereum:
134
+ return { identifier: `${assetString}.${assetString}`, decimal: BaseDecimal[assetString] };
135
+ }
136
+ };
137
+
138
+ export const getAssetType = ({ chain, symbol }: { chain: Chain; symbol: string }) => {
139
+ if (symbol.includes('/')) return 'Synth';
140
+
141
+ switch (chain) {
142
+ case Chain.Bitcoin:
143
+ case Chain.BitcoinCash:
144
+ case Chain.Dogecoin:
145
+ case Chain.Litecoin:
146
+ case Chain.Maya:
147
+ case Chain.THORChain:
148
+ return 'Native';
149
+
150
+ case Chain.Cosmos:
151
+ return symbol === 'ATOM' ? 'Native' : 'GAIA';
152
+ case Chain.Binance:
153
+ return symbol === Chain.Binance ? 'Native' : 'BEP2';
154
+ case Chain.BinanceSmartChain:
155
+ return symbol === Chain.Binance ? 'Native' : 'BEP20';
156
+ case Chain.Ethereum:
157
+ return symbol === Chain.Ethereum ? 'Native' : 'ERC20';
158
+ case Chain.Avalanche:
159
+ return symbol === Chain.Avalanche ? 'Native' : 'AVAX';
160
+ case Chain.Polygon:
161
+ return symbol === Chain.Polygon ? 'Native' : 'POLYGON';
162
+
163
+ case Chain.Arbitrum:
164
+ return [Chain.Ethereum, Chain.Arbitrum].includes(symbol as Chain) ? 'Native' : 'ARBITRUM';
165
+ case Chain.Optimism:
166
+ return [Chain.Ethereum, Chain.Optimism].includes(symbol as Chain) ? 'Native' : 'OPTIMISM';
167
+ }
168
+ };
169
+
170
+ export const assetFromString = (assetString: string) => {
171
+ const [chain, ...symbolArray] = assetString.split('.') as [Chain, ...(string | undefined)[]];
172
+ const synth = assetString.includes('/');
173
+ const symbol = symbolArray.join('.');
174
+ const ticker = symbol?.split('-')?.[0];
175
+
176
+ return { chain, symbol, ticker, synth };
177
+ };
@@ -0,0 +1,157 @@
1
+ import { BaseDecimal } from '@swapkit/types';
2
+
3
+ import { SwapKitNumber } from '../index.ts';
4
+
5
+ type ShareParams<T = {}> = T & {
6
+ liquidityUnits: string;
7
+ poolUnits: string;
8
+ };
9
+
10
+ type PoolParams<T = {}> = T & {
11
+ runeAmount: string;
12
+ assetAmount: string;
13
+ runeDepth: string;
14
+ assetDepth: string;
15
+ };
16
+
17
+ /**
18
+ * Ref: https://gitlab.com/thorchain/thornode/-/issues/657
19
+ * share = (s * A * (2 * T^2 - 2 * T * s + s^2))/T^3
20
+ * s = stakeUnits for member (after factoring in withdrawBasisPoints)
21
+ * T = totalPoolUnits for pool
22
+ * A = assetDepth to be withdrawn
23
+ *
24
+ * Formula:
25
+ * share = (s * A * (2 * T^2 - 2 * T * s + s^2))/T^3
26
+ * (part1 * (part2 - part3 + part4)) / part5
27
+ */
28
+ export const getAsymmetricRuneShare = ({
29
+ liquidityUnits,
30
+ poolUnits,
31
+ runeDepth,
32
+ }: ShareParams<{ runeDepth: string }>) => {
33
+ const s = toTCSwapKitNumber(liquidityUnits);
34
+ const T = toTCSwapKitNumber(poolUnits);
35
+ const A = toTCSwapKitNumber(runeDepth);
36
+
37
+ const part1 = s.mul(A);
38
+ const part2 = T.mul(T).mul(2);
39
+ const part3 = T.mul(s).mul(2);
40
+ const part4 = s.mul(s);
41
+ const part5 = T.mul(T).mul(T);
42
+
43
+ const numerator = part1.mul(part2.sub(part3).add(part4));
44
+
45
+ return numerator.div(part5);
46
+ };
47
+
48
+ export const getAsymmetricAssetShare = ({
49
+ liquidityUnits,
50
+ poolUnits,
51
+ assetDepth,
52
+ }: ShareParams<{ assetDepth: string }>) => {
53
+ const s = toTCSwapKitNumber(liquidityUnits);
54
+ const T = toTCSwapKitNumber(poolUnits);
55
+ const A = toTCSwapKitNumber(assetDepth);
56
+
57
+ const part1 = s.mul(A);
58
+ const part2 = T.mul(T).mul(2);
59
+ const part3 = T.mul(s).mul(2);
60
+ const part4 = s.mul(s);
61
+ const numerator = part1.mul(part2.sub(part3).add(part4));
62
+ const part5 = T.mul(T).mul(T);
63
+
64
+ return numerator.div(part5);
65
+ };
66
+
67
+ export const getAsymmetricRuneWithdrawAmount = ({
68
+ percent,
69
+ runeDepth,
70
+ liquidityUnits,
71
+ poolUnits,
72
+ }: ShareParams<{ percent: number; runeDepth: string }>) =>
73
+ getAsymmetricRuneShare({ runeDepth, liquidityUnits, poolUnits }).mul(percent);
74
+
75
+ export const getAsymmetricAssetWithdrawAmount = ({
76
+ percent,
77
+ assetDepth,
78
+ liquidityUnits,
79
+ poolUnits,
80
+ }: ShareParams<{ percent: number; assetDepth: string }>) =>
81
+ getAsymmetricAssetShare({ assetDepth, liquidityUnits, poolUnits }).mul(percent);
82
+
83
+ const toTCSwapKitNumber = (value: string) =>
84
+ new SwapKitNumber({ value, decimal: BaseDecimal.THOR });
85
+
86
+ export const getSymmetricWithdraw = ({
87
+ liquidityUnits,
88
+ poolUnits,
89
+ runeDepth,
90
+ assetDepth,
91
+ percent,
92
+ }: ShareParams<{
93
+ runeDepth: string;
94
+ assetDepth: string;
95
+ percent: number;
96
+ }>) => ({
97
+ assetAmount: toTCSwapKitNumber(assetDepth).mul(liquidityUnits).div(poolUnits).mul(percent),
98
+ runeAmount: toTCSwapKitNumber(runeDepth).mul(liquidityUnits).div(poolUnits).mul(percent),
99
+ });
100
+
101
+ export const getEstimatedPoolShare = ({
102
+ runeDepth,
103
+ poolUnits,
104
+ assetDepth,
105
+ liquidityUnits,
106
+ runeAmount,
107
+ assetAmount,
108
+ }: ShareParams<{
109
+ runeAmount: string;
110
+ assetAmount: string;
111
+ runeDepth: string;
112
+ assetDepth: string;
113
+ }>) => {
114
+ const R = toTCSwapKitNumber(runeDepth);
115
+ const A = toTCSwapKitNumber(assetDepth);
116
+ const P = toTCSwapKitNumber(poolUnits);
117
+ const runeAddAmount = toTCSwapKitNumber(runeAmount);
118
+ const assetAddAmount = toTCSwapKitNumber(assetAmount);
119
+
120
+ // liquidityUnits = P * (r*A + a*R + 2*r*a) / (r*A + a*R + 2*R*A)
121
+ const rA = runeAddAmount.mul(A);
122
+ const aR = assetAddAmount.mul(R);
123
+ const ra = runeAddAmount.mul(assetAddAmount);
124
+ const RA = R.mul(A);
125
+ const numerator = P.mul(rA.add(aR.add(ra.mul(2))));
126
+ const denominator = rA.add(aR.add(RA.mul(2)));
127
+ const liquidityUnitsAfterAdd = numerator.div(denominator);
128
+ const estimatedLiquidityUnits = toTCSwapKitNumber(liquidityUnits).add(liquidityUnitsAfterAdd);
129
+
130
+ if (liquidityUnitsAfterAdd.baseValueNumber === 0) {
131
+ return estimatedLiquidityUnits.div(P).baseValueNumber;
132
+ }
133
+
134
+ // get pool units after add
135
+ const newPoolUnits = P.add(estimatedLiquidityUnits);
136
+
137
+ return estimatedLiquidityUnits.div(newPoolUnits).baseValueNumber;
138
+ };
139
+
140
+ export const getLiquiditySlippage = ({
141
+ runeAmount,
142
+ assetAmount,
143
+ runeDepth,
144
+ assetDepth,
145
+ }: PoolParams) => {
146
+ // formula: (t * R - T * r)/ (T*r + R*T)
147
+ const R = toTCSwapKitNumber(runeDepth);
148
+ const T = toTCSwapKitNumber(assetDepth);
149
+ const assetAddAmount = toTCSwapKitNumber(assetAmount);
150
+ const runeAddAmount = toTCSwapKitNumber(runeAmount);
151
+
152
+ const numerator = assetAddAmount.mul(R).sub(T.mul(runeAddAmount));
153
+ const denominator = T.mul(runeAddAmount).add(R.mul(T));
154
+
155
+ // set absolute value of percent, no negative allowed
156
+ return Math.abs(numerator.div(denominator).baseValueNumber);
157
+ };