@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,90 @@
1
+ import type { Chain } from '@swapkit/types';
2
+ import { MemoType } from '@swapkit/types';
3
+
4
+ export type ThornameRegisterParam = {
5
+ name: string;
6
+ chain: string;
7
+ address: string;
8
+ owner?: string;
9
+ preferredAsset?: string;
10
+ expiryBlock?: string;
11
+ };
12
+
13
+ const getShortenedSymbol = ({
14
+ symbol,
15
+ ticker,
16
+ chain,
17
+ }: {
18
+ ticker: string;
19
+ symbol: string;
20
+ chain: string | Chain;
21
+ }) => (chain === 'ETH' && ticker !== 'ETH' ? `${ticker}-${symbol.slice(-3)}` : symbol);
22
+
23
+ type WithAddress<T = {}> = T & { address: string };
24
+ type WithChain<T = {}> = T & { chain: Chain };
25
+
26
+ export type MemoOptions<T extends MemoType> = {
27
+ [MemoType.BOND]: WithAddress;
28
+ [MemoType.LEAVE]: WithAddress;
29
+ [MemoType.CLOSE_LOAN]: WithAddress<{ asset: string; minAmount?: string }>;
30
+ [MemoType.OPEN_LOAN]: WithAddress<{ asset: string; minAmount?: string }>;
31
+ [MemoType.UNBOND]: WithAddress<{ unbondAmount: number }>;
32
+ [MemoType.DEPOSIT]: WithChain<{ symbol: string; address?: string; singleSide?: boolean }>;
33
+ [MemoType.WITHDRAW]: WithChain<{
34
+ ticker: string;
35
+ symbol: string;
36
+ basisPoints: number;
37
+ targetAssetString?: string;
38
+ singleSide?: boolean;
39
+ }>;
40
+ [MemoType.THORNAME_REGISTER]: Omit<ThornameRegisterParam, 'preferredAsset' | 'expiryBlock'>;
41
+ }[T];
42
+
43
+ export const getMemoFor = <T extends MemoType>(memoType: T, options: MemoOptions<T>) => {
44
+ switch (memoType) {
45
+ case MemoType.LEAVE:
46
+ case MemoType.BOND: {
47
+ const { address } = options as MemoOptions<MemoType.BOND>;
48
+ return `${memoType}:${address}`;
49
+ }
50
+
51
+ case MemoType.UNBOND: {
52
+ const { address, unbondAmount } = options as MemoOptions<MemoType.UNBOND>;
53
+ return `${memoType}:${address}:${unbondAmount * 10 ** 8}`;
54
+ }
55
+
56
+ case MemoType.THORNAME_REGISTER: {
57
+ const { name, chain, address, owner } = options as MemoOptions<MemoType.THORNAME_REGISTER>;
58
+ return `${memoType}:${name}:${chain}:${address}${owner ? `:${owner}` : ''}`;
59
+ }
60
+
61
+ case MemoType.DEPOSIT: {
62
+ const { chain, symbol, address, singleSide } = options as MemoOptions<MemoType.DEPOSIT>;
63
+
64
+ return singleSide
65
+ ? `${memoType}:${chain}/${symbol}::t:0`
66
+ : `${memoType}:${chain}.${symbol}:${address || ''}:t:0`;
67
+ }
68
+
69
+ case MemoType.WITHDRAW: {
70
+ const { chain, ticker, symbol, basisPoints, targetAssetString, singleSide } =
71
+ options as MemoOptions<MemoType.WITHDRAW>;
72
+
73
+ const target = !singleSide && targetAssetString ? `:${targetAssetString}` : '';
74
+ const shortenedSymbol = getShortenedSymbol({ chain, symbol, ticker });
75
+ const assetDivider = singleSide ? '/' : '.';
76
+
77
+ return `${memoType}:${chain}${assetDivider}${shortenedSymbol}:${basisPoints}${target}`;
78
+ }
79
+
80
+ case MemoType.OPEN_LOAN:
81
+ case MemoType.CLOSE_LOAN: {
82
+ const { asset, address } = options as MemoOptions<MemoType.OPEN_LOAN>;
83
+
84
+ return `${memoType}:${asset}:${address}`; //:${minAmount ? `${minAmount}` : ''}:t:0`;
85
+ }
86
+
87
+ default:
88
+ return '';
89
+ }
90
+ };
@@ -0,0 +1,40 @@
1
+ // THORChain base amount is 1e8
2
+ export const DEFAULT_DECIMAL = 8;
3
+
4
+ export const formatBigIntToSafeValue = ({
5
+ value,
6
+ bigIntDecimal = DEFAULT_DECIMAL,
7
+ decimal = DEFAULT_DECIMAL,
8
+ }: {
9
+ value: bigint;
10
+ bigIntDecimal?: number;
11
+ decimal?: number;
12
+ }) => {
13
+ const isNegative = value < 0n;
14
+ let valueString = value.toString().substring(isNegative ? 1 : 0);
15
+
16
+ const padLength = decimal - (valueString.length - 1);
17
+
18
+ if (padLength > 0) {
19
+ valueString = '0'.repeat(padLength) + valueString;
20
+ }
21
+
22
+ const decimalIndex = valueString.length - decimal;
23
+ let decimalString = valueString.slice(-decimal);
24
+
25
+ // Check if we need to round up
26
+ if (parseInt(decimalString[bigIntDecimal]) >= 5) {
27
+ // Increment the last decimal place and slice off the rest
28
+ decimalString = `${decimalString.substring(0, bigIntDecimal - 1)}${(
29
+ parseInt(decimalString[bigIntDecimal - 1]) + 1
30
+ ).toString()}`;
31
+ } else {
32
+ // Just slice off the extra digits
33
+ decimalString = decimalString.substring(0, bigIntDecimal);
34
+ }
35
+
36
+ return `${isNegative ? '-' : ''}${valueString.slice(0, decimalIndex)}.${decimalString}`.replace(
37
+ /\.?0*$/,
38
+ '',
39
+ );
40
+ };
@@ -0,0 +1,59 @@
1
+ // 10 rune for register, 1 rune per year
2
+ // MINIMUM_REGISTRATION_FEE = 11
3
+ export const getTHORNameCost = (year: number) => {
4
+ if (year < 0) throw new Error('Invalid number of year');
5
+ return 10 + year;
6
+ };
7
+
8
+ export const validateTHORName = (name: string) => {
9
+ if (name.length > 30) return false;
10
+
11
+ const regex = /^[a-zA-Z0-9+_-]+$/g;
12
+
13
+ return !!name.match(regex);
14
+ };
15
+
16
+ export const derivationPathToString = ([network, chainId, account, change, index]: number[]) => {
17
+ const shortPath = typeof index !== 'number';
18
+
19
+ return `${network}'/${chainId}'/${account}'/${change}${shortPath ? '' : `/${index}`}`;
20
+ };
21
+
22
+ export const getRequest = async <T>(
23
+ url: string,
24
+ params?: { [key in string]?: any },
25
+ ): Promise<T> => {
26
+ const queryParams = Object.entries(params || {}).reduce(
27
+ (acc, [key, value]) => {
28
+ if (value) {
29
+ acc[key] = value;
30
+ }
31
+
32
+ return acc;
33
+ },
34
+ {} as { [key in string]: any },
35
+ );
36
+
37
+ const response = await fetch(
38
+ `${url}${params ? `?${new URLSearchParams(queryParams).toString()}` : ''}`,
39
+ { method: 'GET', mode: 'cors', credentials: 'omit', referrer: 'https://sk.thorswap.net' },
40
+ );
41
+
42
+ return response.json();
43
+ };
44
+
45
+ export const postRequest = async <T>(
46
+ url: string,
47
+ body: string,
48
+ headers?: Record<string, string>,
49
+ parseAsString = false,
50
+ ): Promise<T> => {
51
+ const response = await fetch(`${url}`, {
52
+ body,
53
+ headers,
54
+ method: 'POST',
55
+ referrer: 'https://sk.thorswap.net',
56
+ });
57
+
58
+ return parseAsString ? response.text() : response.json();
59
+ };
@@ -0,0 +1,17 @@
1
+ import { Chain } from '@swapkit/types';
2
+
3
+ const supportedChains = Object.values(Chain);
4
+
5
+ export const validateIdentifier = (identifier: string = '') => {
6
+ const uppercasedIdentifier = identifier.toUpperCase();
7
+
8
+ const [chain] = uppercasedIdentifier.split('.') as [Chain, string];
9
+ if (supportedChains.includes(chain)) return true;
10
+
11
+ const [synthChain] = uppercasedIdentifier.split('/') as [Chain, string];
12
+ if (supportedChains.includes(synthChain)) return true;
13
+
14
+ throw new Error(
15
+ `Invalid identifier: ${identifier}. Expected format: <Chain>.<Ticker> or <Chain>.<Ticker>-<ContractAddress>`,
16
+ );
17
+ };
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Helpers
3
+ */
4
+ export * from './helpers/asset.ts';
5
+ export * from './helpers/liquidity.ts';
6
+ export * from './helpers/memo.ts';
7
+ export * from './helpers/number.ts';
8
+ export * from './helpers/others.ts';
9
+
10
+ /**
11
+ * Modules
12
+ */
13
+ export { AssetValue, getMinAmountByChain } from './modules/assetValue.ts';
14
+ export { type Keys, SwapKitError } from './modules/swapKitError.ts';
15
+ export { SwapKitNumber } from './modules/swapKitNumber.ts';
@@ -0,0 +1,285 @@
1
+ import { BaseDecimal, Chain } from '@swapkit/types';
2
+ import { describe, expect, test } from 'vitest';
3
+
4
+ import { AssetValue } from '../assetValue.ts';
5
+
6
+ describe('AssetValue', () => {
7
+ describe('assetValue', () => {
8
+ test('returns asset ticker with value', () => {
9
+ const fakeAvaxUSDCAsset = new AssetValue({
10
+ decimal: 6,
11
+ value: 1234567890,
12
+ chain: Chain.Avalanche,
13
+ symbol: 'USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
14
+ });
15
+ expect(fakeAvaxUSDCAsset.assetValue).toBe('1234567890 USDC');
16
+
17
+ const thor = AssetValue.fromChainOrSignature('ETH.THOR');
18
+ expect(thor.assetValue).toBe('0 THOR');
19
+
20
+ const ethSynth = new AssetValue({
21
+ chain: Chain.THORChain,
22
+ symbol: 'ETH/ETH',
23
+ decimal: 18,
24
+ value: 1234567890,
25
+ });
26
+ expect(ethSynth.assetValue).toBe('1234567890 ETH/ETH');
27
+ expect(ethSynth.toString()).toBe('THOR.ETH/ETH');
28
+ });
29
+ });
30
+
31
+ describe('eq', () => {
32
+ test('checks if assets are same chain and symbol', () => {
33
+ const firstThor = AssetValue.fromChainOrSignature('ETH.THOR');
34
+ const secondThor = AssetValue.fromChainOrSignature('ETH.THOR');
35
+ const vThor = AssetValue.fromChainOrSignature('ETH.vTHOR');
36
+ const firstUsdc = new AssetValue({
37
+ chain: Chain.Avalanche,
38
+ symbol: 'USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
39
+ decimal: 6,
40
+ value: 1234567890,
41
+ });
42
+ const secondUsdc = new AssetValue({
43
+ chain: Chain.Avalanche,
44
+ symbol: 'USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
45
+ decimal: 6,
46
+ value: 1234,
47
+ });
48
+
49
+ expect(firstThor.eq(firstThor)).toBe(true);
50
+ expect(firstThor.eq(secondThor)).toBe(true);
51
+ expect(firstThor.eq(vThor)).toBe(false);
52
+ expect(firstThor.eq(firstUsdc)).toBe(false);
53
+ expect(firstThor.eq(secondUsdc)).toBe(false);
54
+
55
+ expect(firstUsdc.eq(firstThor)).toBe(false);
56
+ expect(firstUsdc.eq(secondThor)).toBe(false);
57
+ expect(firstUsdc.eq(vThor)).toBe(false);
58
+ expect(firstUsdc.eq(firstUsdc)).toBe(true);
59
+ expect(firstUsdc.eq(secondUsdc)).toBe(true);
60
+ });
61
+ });
62
+
63
+ describe('toString', () => {
64
+ test('returns asset value string/identifier', async () => {
65
+ const fakeAvaxUSDCAsset = new AssetValue({
66
+ decimal: 6,
67
+ value: 1234567890,
68
+ chain: Chain.Avalanche,
69
+ symbol: 'USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
70
+ });
71
+ expect(fakeAvaxUSDCAsset.toString()).toBe(
72
+ 'AVAX.USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
73
+ );
74
+
75
+ const thor = AssetValue.fromChainOrSignature('ETH.THOR');
76
+ expect(thor.toString()).toBe('ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044');
77
+
78
+ const ethSynth = await AssetValue.fromIdentifier('ETH/ETH');
79
+ expect(ethSynth.toString()).toBe('THOR.ETH/ETH');
80
+ });
81
+ });
82
+
83
+ describe('fromIdentifier', () => {
84
+ test('creates AssetValue from string', async () => {
85
+ const fakeAvaxUSDCAssetString = 'AVAX.USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e';
86
+ const fakeAvaxUSDCAsset = await AssetValue.fromIdentifier(fakeAvaxUSDCAssetString);
87
+
88
+ expect(fakeAvaxUSDCAsset).toEqual(
89
+ expect.objectContaining({
90
+ address: '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
91
+ chain: Chain.Avalanche,
92
+ decimal: 6,
93
+ isGasAsset: false,
94
+ isSynthetic: false,
95
+ symbol: 'USDC-0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
96
+ ticker: 'USDC',
97
+ }),
98
+ );
99
+ });
100
+ });
101
+
102
+ describe('fromString', () => {
103
+ test('creates AssetValue from string', () => {
104
+ test('creates AssetValue from string', async () => {
105
+ const fakeAvaxAssetString = 'AVAX.ASDF-1234';
106
+ const fakeAvaxAsset = await AssetValue.fromString(fakeAvaxAssetString);
107
+
108
+ expect(fakeAvaxAsset).toEqual(
109
+ expect.objectContaining({
110
+ address: '1234',
111
+ chain: Chain.Avalanche,
112
+ decimal: 10,
113
+ isGasAsset: false,
114
+ isSynthetic: false,
115
+ symbol: 'ASDF-1234',
116
+ ticker: 'ASDF',
117
+ }),
118
+ );
119
+ });
120
+ });
121
+ });
122
+
123
+ describe('fromIdentifierSync', () => {
124
+ test('(same as fromIdentifier) - creates AssetValue from string via `@swapkit/tokens` lists', async () => {
125
+ await AssetValue.loadStaticAssets();
126
+ const thor = AssetValue.fromIdentifierSync(
127
+ 'ARB.USDT-0XFD086BC7CD5C481DCC9C85EBE478A1C0B69FCBB9',
128
+ );
129
+
130
+ expect(thor).toBeDefined();
131
+ expect(thor).toEqual(
132
+ expect.objectContaining({
133
+ address: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
134
+ chain: Chain.Arbitrum,
135
+ decimal: 6,
136
+ isGasAsset: false,
137
+ isSynthetic: false,
138
+ symbol: 'USDT-0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
139
+ ticker: 'USDT',
140
+ }),
141
+ );
142
+ });
143
+ });
144
+
145
+ describe('fromStringSync', () => {
146
+ test('creates AssetValue from string via `@swapkit/tokens` lists', async () => {
147
+ await AssetValue.loadStaticAssets();
148
+ const thor = AssetValue.fromStringSync('ETH.THOR-0xa5f2211b9b8170f694421f2046281775e8468044');
149
+
150
+ expect(thor).toBeDefined();
151
+ expect(thor).toEqual(
152
+ expect.objectContaining({
153
+ address: '0xa5f2211b9b8170f694421f2046281775e8468044',
154
+ chain: Chain.Ethereum,
155
+ decimal: 18,
156
+ isGasAsset: false,
157
+ isSynthetic: false,
158
+ symbol: 'THOR-0xa5f2211b9b8170f694421f2046281775e8468044',
159
+ ticker: 'THOR',
160
+ }),
161
+ );
162
+ });
163
+
164
+ test('returns undefined if string is not in `@swapkit/tokens` lists', async () => {
165
+ await AssetValue.loadStaticAssets();
166
+ const fakeAvaxUSDCAssetString = 'AVAX.USDC-1234';
167
+ const fakeAvaxUSDCAsset = AssetValue.fromStringSync(fakeAvaxUSDCAssetString);
168
+
169
+ expect(fakeAvaxUSDCAsset).toBeUndefined();
170
+ });
171
+ });
172
+
173
+ describe('fromChainOrSignature', () => {
174
+ test('creates AssetValue from common asset string or chain', () => {
175
+ const customBaseAsset = [Chain.Cosmos, Chain.BinanceSmartChain, Chain.THORChain, Chain.Maya];
176
+ Object.values(Chain)
177
+ .filter((c) => !customBaseAsset.includes(c))
178
+ .forEach((chain) => {
179
+ const asset = AssetValue.fromChainOrSignature(chain);
180
+ expect(asset).toEqual(
181
+ expect.objectContaining({
182
+ address: undefined,
183
+ chain,
184
+ decimal: BaseDecimal[chain],
185
+ isGasAsset: ![Chain.Arbitrum, Chain.Optimism].includes(chain),
186
+ isSynthetic: false,
187
+ symbol: chain,
188
+ ticker: chain,
189
+ type: 'Native',
190
+ }),
191
+ );
192
+ });
193
+
194
+ const cosmosAsset = AssetValue.fromChainOrSignature(Chain.Cosmos);
195
+ expect(cosmosAsset).toEqual(
196
+ expect.objectContaining({
197
+ address: undefined,
198
+ chain: Chain.Cosmos,
199
+ decimal: BaseDecimal.GAIA,
200
+ isGasAsset: true,
201
+ isSynthetic: false,
202
+ symbol: 'ATOM',
203
+ ticker: 'ATOM',
204
+ type: 'Native',
205
+ }),
206
+ );
207
+
208
+ const bscAsset = AssetValue.fromChainOrSignature(Chain.BinanceSmartChain);
209
+ expect(bscAsset).toEqual(
210
+ expect.objectContaining({
211
+ address: undefined,
212
+ chain: Chain.BinanceSmartChain,
213
+ decimal: BaseDecimal.BSC,
214
+ isGasAsset: true,
215
+ isSynthetic: false,
216
+ symbol: 'BNB',
217
+ ticker: 'BNB',
218
+ type: 'Native',
219
+ }),
220
+ );
221
+
222
+ const thorAsset = AssetValue.fromChainOrSignature(Chain.THORChain);
223
+ expect(thorAsset).toEqual(
224
+ expect.objectContaining({
225
+ address: undefined,
226
+ chain: Chain.THORChain,
227
+ decimal: BaseDecimal.THOR,
228
+ isGasAsset: true,
229
+ isSynthetic: false,
230
+ symbol: 'RUNE',
231
+ ticker: 'RUNE',
232
+ type: 'Native',
233
+ }),
234
+ );
235
+
236
+ const cacaoAsset = AssetValue.fromChainOrSignature(Chain.Maya);
237
+ expect(cacaoAsset).toEqual(
238
+ expect.objectContaining({
239
+ address: undefined,
240
+ chain: Chain.Maya,
241
+ decimal: BaseDecimal.MAYA,
242
+ isGasAsset: true,
243
+ isSynthetic: false,
244
+ symbol: 'CACAO',
245
+ ticker: 'CACAO',
246
+ type: 'Native',
247
+ }),
248
+ );
249
+
250
+ const thor = AssetValue.fromChainOrSignature('ETH.THOR');
251
+ expect(thor).toEqual(
252
+ expect.objectContaining({
253
+ address: '0xa5f2211b9b8170f694421f2046281775e8468044',
254
+ chain: Chain.Ethereum,
255
+ decimal: 18,
256
+ isGasAsset: false,
257
+ isSynthetic: false,
258
+ symbol: 'THOR-0xa5f2211b9b8170f694421f2046281775e8468044',
259
+ ticker: 'THOR',
260
+ }),
261
+ );
262
+
263
+ const vthor = AssetValue.fromChainOrSignature('ETH.vTHOR');
264
+ expect(vthor).toEqual(
265
+ expect.objectContaining({
266
+ address: '0x815c23eca83261b6ec689b60cc4a58b54bc24d8d',
267
+ chain: Chain.Ethereum,
268
+ decimal: 18,
269
+ isGasAsset: false,
270
+ isSynthetic: false,
271
+ symbol: 'vTHOR-0x815c23eca83261b6ec689b60cc4a58b54bc24d8d',
272
+ ticker: 'vTHOR',
273
+ }),
274
+ );
275
+ });
276
+ });
277
+
278
+ describe('loadStaticAssets', () => {
279
+ test('loads static assets from `@swapkit/tokens` lists', async () => {
280
+ // Dummy test - think of sth more meaningful
281
+ const { ok } = await AssetValue.loadStaticAssets();
282
+ expect(ok).toBe(true);
283
+ });
284
+ });
285
+ });