@swapkit/helpers 4.4.5 → 4.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/api/index.cjs +2 -2
  2. package/dist/api/index.cjs.map +4 -4
  3. package/dist/api/index.js +2 -2
  4. package/dist/api/index.js.map +4 -4
  5. package/dist/index.cjs +3 -3
  6. package/dist/index.cjs.map +10 -10
  7. package/dist/index.js +3 -3
  8. package/dist/index.js.map +10 -10
  9. package/dist/types/api/index.d.ts +247 -0
  10. package/dist/types/api/index.d.ts.map +1 -1
  11. package/dist/types/api/swapkitApi/endpoints.d.ts +247 -0
  12. package/dist/types/api/swapkitApi/endpoints.d.ts.map +1 -1
  13. package/dist/types/api/swapkitApi/types.d.ts +3 -1
  14. package/dist/types/api/swapkitApi/types.d.ts.map +1 -1
  15. package/dist/types/modules/assetValue.d.ts +6 -0
  16. package/dist/types/modules/assetValue.d.ts.map +1 -1
  17. package/dist/types/modules/bigIntArithmetics.d.ts.map +1 -1
  18. package/dist/types/modules/swapKitConfig.d.ts +63 -36
  19. package/dist/types/modules/swapKitConfig.d.ts.map +1 -1
  20. package/dist/types/modules/swapKitError.d.ts +20 -24
  21. package/dist/types/modules/swapKitError.d.ts.map +1 -1
  22. package/dist/types/types/wallet.d.ts +5 -1
  23. package/dist/types/types/wallet.d.ts.map +1 -1
  24. package/dist/types/utils/asset.d.ts +1 -1
  25. package/dist/types/utils/asset.d.ts.map +1 -1
  26. package/package.json +24 -12
  27. package/src/api/index.ts +9 -0
  28. package/src/api/midgard/endpoints.ts +348 -0
  29. package/src/api/midgard/types.ts +515 -0
  30. package/src/api/swapkitApi/endpoints.ts +242 -0
  31. package/src/api/swapkitApi/types.ts +624 -0
  32. package/src/api/thornode/endpoints.ts +105 -0
  33. package/src/api/thornode/types.ts +247 -0
  34. package/src/contracts.ts +1 -0
  35. package/src/index.ts +28 -0
  36. package/src/modules/__tests__/assetValue.test.ts +1637 -0
  37. package/src/modules/__tests__/bigIntArithmetics.test.ts +383 -0
  38. package/src/modules/__tests__/swapKitConfig.test.ts +425 -0
  39. package/src/modules/__tests__/swapKitNumber.test.ts +535 -0
  40. package/src/modules/assetValue.ts +532 -0
  41. package/src/modules/bigIntArithmetics.ts +363 -0
  42. package/src/modules/feeMultiplier.ts +80 -0
  43. package/src/modules/requestClient.ts +110 -0
  44. package/src/modules/swapKitConfig.ts +174 -0
  45. package/src/modules/swapKitError.ts +470 -0
  46. package/src/modules/swapKitNumber.ts +13 -0
  47. package/src/tokens.ts +1 -0
  48. package/src/types/commonTypes.ts +10 -0
  49. package/src/types/derivationPath.ts +11 -0
  50. package/src/types/errors/apiV1.ts +0 -0
  51. package/src/types/index.ts +5 -0
  52. package/src/types/quotes.ts +174 -0
  53. package/src/types/sdk.ts +38 -0
  54. package/src/types/wallet.ts +124 -0
  55. package/src/utils/__tests__/asset.test.ts +185 -0
  56. package/src/utils/__tests__/derivationPath.test.ts +16 -0
  57. package/src/utils/__tests__/explorerUrls.test.ts +59 -0
  58. package/src/utils/__tests__/memo.test.ts +99 -0
  59. package/src/utils/__tests__/others.test.ts +83 -0
  60. package/src/utils/__tests__/validators.test.ts +24 -0
  61. package/src/utils/asset.ts +395 -0
  62. package/src/utils/chains.ts +100 -0
  63. package/src/utils/derivationPath.ts +101 -0
  64. package/src/utils/explorerUrls.ts +32 -0
  65. package/src/utils/liquidity.ts +150 -0
  66. package/src/utils/memo.ts +102 -0
  67. package/src/utils/others.ts +62 -0
  68. package/src/utils/validators.ts +32 -0
  69. package/src/utils/wallets.ts +237 -0
@@ -0,0 +1,100 @@
1
+ import { Chain, CosmosChains, EVMChains, type StagenetChain, StagenetChains, UTXOChains } from "@swapkit/types";
2
+ import { match } from "ts-pattern";
3
+ import { SKConfig } from "../modules/swapKitConfig";
4
+ import { SwapKitError } from "../modules/swapKitError";
5
+ import { warnOnce } from "./others";
6
+
7
+ function getRpcBody(chain: Chain | StagenetChain) {
8
+ return match(chain)
9
+ .with(...EVMChains, () => ({ id: 1, jsonrpc: "2.0", method: "eth_blockNumber", params: [] }))
10
+ .with(...UTXOChains, () => ({ id: "test", jsonrpc: "1.0", method: "getblockchaininfo", params: [] }))
11
+ .with(...CosmosChains, ...StagenetChains, () => ({ id: 1, jsonrpc: "2.0", method: "status", params: {} }))
12
+ .with(Chain.Polkadot, Chain.Chainflip, () => ({ id: 1, jsonrpc: "2.0", method: "system_health", params: [] }))
13
+ .with(Chain.Solana, () => ({ id: 1, jsonrpc: "2.0", method: "getHealth" }))
14
+ .with(Chain.Sui, () => ({ id: 1, jsonrpc: "2.0", method: "sui_getSystemState", params: [] }))
15
+ .with(Chain.Ton, () => ({ id: 1, jsonrpc: "2.0", method: "getAddressInformation", params: { address: "" } }))
16
+ .with(Chain.Tron, Chain.Radix, () => "")
17
+ .with(Chain.Near, () => ({ id: "dontcare", jsonrpc: "2.0", method: "status", params: [] }))
18
+ .with(Chain.Ripple, () => ({ id: 1, jsonrpc: "2.0", method: "ping", params: [{}] }))
19
+ .otherwise(() => {
20
+ throw new SwapKitError("helpers_chain_not_supported", { chain });
21
+ });
22
+ }
23
+
24
+ function getChainStatusEndpoint(chain: Chain | StagenetChain) {
25
+ return match(chain)
26
+ .with(Chain.Radix, () => "/status/network-configuration")
27
+ .with(Chain.Tron, () => "/wallet/getnowblock")
28
+ .otherwise(() => "");
29
+ }
30
+
31
+ async function testRPCConnection(chain: Chain | StagenetChain, url: string) {
32
+ try {
33
+ const endpoint = url.startsWith("wss") ? url.replace("wss", "https") : url;
34
+ const response = await fetch(`${endpoint}${getChainStatusEndpoint(chain)}`, {
35
+ body: JSON.stringify(getRpcBody(chain)),
36
+ headers: { "Content-Type": "application/json" },
37
+ method: "POST",
38
+ signal: AbortSignal.timeout(3000),
39
+ });
40
+
41
+ return response.ok;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ const rpcCache = new Map<Chain | StagenetChain, { timestamp: number; url: string }>();
48
+ const rpcCacheTTL = 1000 * 60 * 2; // 2 minutes
49
+
50
+ export async function getRPCUrl(chain: Chain | StagenetChain) {
51
+ const { isStagenet } = SKConfig.get("envs");
52
+ const [rpcUrl = "", ...fallbackUrls] = SKConfig.get("rpcUrls")[chain];
53
+
54
+ if (!rpcUrl) {
55
+ warnOnce({
56
+ condition: true,
57
+ id: "helpers_chain_no_public_or_set_rpc_url",
58
+ warning: `No public or set RPC URL found for chain. Please ensure you configured rpcUrls for ${chain}.`,
59
+ });
60
+ throw new SwapKitError("helpers_chain_no_public_or_set_rpc_url", { chain });
61
+ }
62
+
63
+ if (isStagenet) return rpcUrl;
64
+
65
+ const cached = rpcCache.get(chain);
66
+ if (cached && Date.now() - cached.timestamp < rpcCacheTTL) {
67
+ return cached.url;
68
+ }
69
+
70
+ const primaryIsWorking = await testRPCConnection(chain, rpcUrl);
71
+
72
+ if (!primaryIsWorking) {
73
+ for (const fallbackUrl of fallbackUrls) {
74
+ const fallbackIsWorking = await testRPCConnection(chain, fallbackUrl);
75
+
76
+ if (fallbackIsWorking) {
77
+ rpcCache.set(chain, { timestamp: Date.now(), url: fallbackUrl });
78
+ return fallbackUrl;
79
+ }
80
+ }
81
+ }
82
+
83
+ rpcCache.set(chain, { timestamp: Date.now(), url: rpcUrl });
84
+ return rpcUrl;
85
+ }
86
+
87
+ /**
88
+ * @deprecated
89
+ * RPC URLs are now managed dynamically via SKConfig.
90
+ * Please use static { rpcUrls, fallbackRpcUrls } SwapKit init config or dynamic SKConfig.setRpcUrl/setFallbackRpcUrl to configure RPC endpoints.
91
+ * This function is obsolete and will be removed in a future release.
92
+ */
93
+ export function initializeRPCUrlsWithFallback(_chains: never) {
94
+ warnOnce({
95
+ condition: true,
96
+ id: "initializeRPCUrlsWithFallback",
97
+ warning:
98
+ "initializeRPCUrlsWithFallback is deprecated. Use static { rpcUrls, fallbackRpcUrls } SwapKit init config or dynamic SKConfig.setRpcUrl/setFallbackRpcUrl to configure RPC endpoints.",
99
+ });
100
+ }
@@ -0,0 +1,101 @@
1
+ import { Chain, type EVMChain, EVMChains } from "@swapkit/types";
2
+ import { type DerivationPathArray, NetworkDerivationPath } from "../types";
3
+
4
+ type Params = {
5
+ chain: Chain;
6
+ index: number;
7
+ addressIndex?: number;
8
+ type?: "legacy" | "ledgerLive" | "nativeSegwitMiddleAccount" | "segwit" | "account";
9
+ };
10
+
11
+ export function updateDerivationPath(
12
+ path: DerivationPathArray,
13
+ params: { index: number } | { account: number } | { change: number },
14
+ ) {
15
+ if ("index" in params) {
16
+ const newPath = [...path.slice(0, path.length - 1), params.index];
17
+ return newPath as DerivationPathArray;
18
+ }
19
+
20
+ if ("change" in params) {
21
+ const [network, chainId, account, , index] = path;
22
+ return [network, chainId, account, params.change, index] as DerivationPathArray;
23
+ }
24
+
25
+ if ("account" in params) {
26
+ const [network, chainId, , change, index] = path;
27
+ return [network, chainId, params.account, change, index] as DerivationPathArray;
28
+ }
29
+
30
+ return path;
31
+ }
32
+
33
+ export function derivationPathToString([network, chainId, account, change, index]:
34
+ | [number, number, number, number?, number?]
35
+ | [number, number, number, number, number?]
36
+ | DerivationPathArray) {
37
+ const shortPath = typeof index !== "number";
38
+ const accountPath = typeof change !== "number";
39
+
40
+ if (accountPath) {
41
+ return `m/${network}'/${chainId}'/${account}'`;
42
+ }
43
+
44
+ return `m/${network}'/${chainId}'/${account}'/${change}${shortPath ? "" : `/${index}`}`;
45
+ }
46
+
47
+ // TODO @towan - sort this out and make it more readable
48
+ export function getDerivationPathFor({ chain, index, addressIndex = 0, type }: Params) {
49
+ if (EVMChains.includes(chain as EVMChain)) {
50
+ if (type && ["legacy", "account"].includes(type)) {
51
+ return [44, 60, 0, index];
52
+ }
53
+
54
+ if (type === "ledgerLive") {
55
+ return [44, 60, index, 0, addressIndex] as DerivationPathArray;
56
+ }
57
+
58
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
59
+ }
60
+
61
+ if (chain === Chain.Solana) {
62
+ if (type === "account") return [44, 501, 0, index];
63
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
64
+ }
65
+
66
+ const chainId = chain === Chain.Litecoin ? 2 : 0;
67
+
68
+ switch (type) {
69
+ case "nativeSegwitMiddleAccount":
70
+ return [84, chainId, index, 0, addressIndex];
71
+ case "segwit":
72
+ return [49, chainId, 0, 0, index];
73
+ case "legacy":
74
+ return [44, chainId, 0, 0, index];
75
+ default:
76
+ return updateDerivationPath(NetworkDerivationPath[chain], { index });
77
+ }
78
+ }
79
+
80
+ export function getWalletFormatFor(path: string) {
81
+ const [_, purpose, chainId] = path.split("/").map((p) => Number.parseInt(p, 10));
82
+
83
+ if (chainId === 145) "cashaddr";
84
+
85
+ switch (purpose) {
86
+ case 44:
87
+ return "legacy";
88
+ case 49:
89
+ return "p2sh";
90
+ default:
91
+ return "bech32";
92
+ }
93
+ }
94
+
95
+ export const DerivationPath: Record<Chain, string> = Object.keys(NetworkDerivationPath).reduce(
96
+ (acc, key) => {
97
+ acc[key as Chain] = derivationPathToString(NetworkDerivationPath[key as Chain]);
98
+ return acc;
99
+ },
100
+ {} as Record<Chain, string>,
101
+ );
@@ -0,0 +1,32 @@
1
+ import { Chain, CosmosChains, EVMChains, getChainConfig, SubstrateChains, UTXOChains } from "@swapkit/types";
2
+ import { match } from "ts-pattern";
3
+
4
+ export function getExplorerTxUrl({ chain, txHash }: { txHash: string; chain: Chain }) {
5
+ const { explorerUrl } = getChainConfig(chain);
6
+
7
+ return match(chain)
8
+ .with(
9
+ ...CosmosChains,
10
+ Chain.Solana,
11
+ () => `${explorerUrl}/tx/${txHash.startsWith("0x") ? txHash.slice(2) : txHash}`,
12
+ )
13
+ .with(
14
+ ...EVMChains,
15
+ ...SubstrateChains,
16
+ () => `${explorerUrl}/tx/${txHash.startsWith("0x") ? txHash : `0x${txHash}`}`,
17
+ )
18
+ .with(...UTXOChains, Chain.Radix, Chain.Tron, () => `${explorerUrl}/transaction/${txHash.toLowerCase()}`)
19
+ .with(Chain.Near, () => `${explorerUrl}/txns/${txHash}`)
20
+ .with(Chain.Ripple, () => `${explorerUrl}/transactions/${txHash}`)
21
+ .with(Chain.Sui, () => `${explorerUrl}/txblock/${txHash}`)
22
+ .with(Chain.Cardano, Chain.Ton, () => `${explorerUrl}/tx/${txHash}`)
23
+ .otherwise(() => "");
24
+ }
25
+
26
+ export function getExplorerAddressUrl({ chain, address }: { address: string; chain: Chain }) {
27
+ const { explorerUrl } = getChainConfig(chain);
28
+
29
+ return match(chain)
30
+ .with(Chain.Solana, Chain.Sui, Chain.Radix, () => `${explorerUrl}/account/${address}`)
31
+ .otherwise(() => `${explorerUrl}/address/${address}`);
32
+ }
@@ -0,0 +1,150 @@
1
+ import { Chain, getChainConfig } from "@swapkit/types";
2
+ import { SwapKitNumber } from "../modules/swapKitNumber";
3
+
4
+ type ShareParams<T extends {}> = T & { liquidityUnits: string; poolUnits: string };
5
+
6
+ type PoolParams = { runeAmount: string; assetAmount: string; runeDepth: string; assetDepth: string };
7
+
8
+ /**
9
+ * Ref: https://gitlab.com/thorchain/thornode/-/issues/657
10
+ * share = (s * A * (2 * T^2 - 2 * T * s + s^2))/T^3
11
+ * s = stakeUnits for member (after factoring in withdrawBasisPoints)
12
+ * T = totalPoolUnits for pool
13
+ * A = assetDepth to be withdrawn
14
+ *
15
+ * Formula:
16
+ * share = (s * A * (2 * T^2 - 2 * T * s + s^2))/T^3
17
+ * (part1 * (part2 - part3 + part4)) / part5
18
+ */
19
+ export function getAsymmetricRuneShare({ liquidityUnits, poolUnits, runeDepth }: ShareParams<{ runeDepth: string }>) {
20
+ const s = toTCSwapKitNumber(liquidityUnits);
21
+ const T = toTCSwapKitNumber(poolUnits);
22
+ const A = toTCSwapKitNumber(runeDepth);
23
+
24
+ const part1 = s.mul(A);
25
+ const part2 = T.mul(T).mul(2);
26
+ const part3 = T.mul(s).mul(2);
27
+ const part4 = s.mul(s);
28
+ const part5 = T.mul(T).mul(T);
29
+
30
+ const numerator = part1.mul(part2.sub(part3).add(part4));
31
+
32
+ return numerator.div(part5);
33
+ }
34
+
35
+ export function getAsymmetricAssetShare({
36
+ liquidityUnits,
37
+ poolUnits,
38
+ assetDepth,
39
+ }: ShareParams<{ assetDepth: string }>) {
40
+ const s = toTCSwapKitNumber(liquidityUnits);
41
+ const T = toTCSwapKitNumber(poolUnits);
42
+ const A = toTCSwapKitNumber(assetDepth);
43
+
44
+ const part1 = s.mul(A);
45
+ const part2 = T.mul(T).mul(2);
46
+ const part3 = T.mul(s).mul(2);
47
+ const part4 = s.mul(s);
48
+ const numerator = part1.mul(part2.sub(part3).add(part4));
49
+ const part5 = T.mul(T).mul(T);
50
+
51
+ return numerator.div(part5);
52
+ }
53
+
54
+ export function getAsymmetricRuneWithdrawAmount({
55
+ percent,
56
+ runeDepth,
57
+ liquidityUnits,
58
+ poolUnits,
59
+ }: ShareParams<{ percent: number; runeDepth: string }>) {
60
+ return getAsymmetricRuneShare({ liquidityUnits, poolUnits, runeDepth }).mul(percent);
61
+ }
62
+
63
+ export function getAsymmetricAssetWithdrawAmount({
64
+ percent,
65
+ assetDepth,
66
+ liquidityUnits,
67
+ poolUnits,
68
+ }: ShareParams<{ percent: number; assetDepth: string }>) {
69
+ return getAsymmetricAssetShare({ assetDepth, liquidityUnits, poolUnits }).mul(percent);
70
+ }
71
+
72
+ function toTCSwapKitNumber(value: string) {
73
+ return SwapKitNumber.fromBigInt(BigInt(value), getChainConfig(Chain.THORChain).baseDecimal);
74
+ }
75
+
76
+ export function getSymmetricPoolShare({
77
+ liquidityUnits,
78
+ poolUnits,
79
+ runeDepth,
80
+ assetDepth,
81
+ }: ShareParams<{ runeDepth: string; assetDepth: string }>) {
82
+ return {
83
+ assetAmount: toTCSwapKitNumber(assetDepth).mul(liquidityUnits).div(poolUnits),
84
+ runeAmount: toTCSwapKitNumber(runeDepth).mul(liquidityUnits).div(poolUnits),
85
+ };
86
+ }
87
+
88
+ export function getSymmetricWithdraw({
89
+ liquidityUnits,
90
+ poolUnits,
91
+ runeDepth,
92
+ assetDepth,
93
+ percent,
94
+ }: ShareParams<{ runeDepth: string; assetDepth: string; percent: number }>) {
95
+ return Object.fromEntries(
96
+ Object.entries(getSymmetricPoolShare({ assetDepth, liquidityUnits, poolUnits, runeDepth })).map(([name, value]) => [
97
+ name,
98
+ value.mul(percent),
99
+ ]),
100
+ );
101
+ }
102
+
103
+ export function getEstimatedPoolShare({
104
+ runeDepth,
105
+ poolUnits,
106
+ assetDepth,
107
+ liquidityUnits,
108
+ runeAmount,
109
+ assetAmount,
110
+ }: ShareParams<{ runeAmount: string; assetAmount: string; runeDepth: string; assetDepth: string }>) {
111
+ const R = new SwapKitNumber({ decimal: 8, value: runeDepth });
112
+ const A = new SwapKitNumber({ decimal: 8, value: assetDepth });
113
+ const P = new SwapKitNumber({ decimal: 8, value: poolUnits });
114
+ const runeAddAmount = new SwapKitNumber({ decimal: 8, value: runeAmount });
115
+ const assetAddAmount = new SwapKitNumber({ decimal: 8, value: assetAmount });
116
+
117
+ // liquidityUnits = P * (r*A + a*R + 2*r*a) / (r*A + a*R + 2*R*A)
118
+ const rA = runeAddAmount.mul(A);
119
+ const aR = assetAddAmount.mul(R);
120
+ const ra = runeAddAmount.mul(assetAddAmount);
121
+ const RA = R.mul(A);
122
+ const numerator = P.mul(rA.add(aR.add(ra.mul(2))));
123
+ const denominator = rA.add(aR.add(RA.mul(2)));
124
+ const liquidityUnitsAfterAdd = numerator.div(denominator);
125
+ const estimatedLiquidityUnits = toTCSwapKitNumber(liquidityUnits).add(liquidityUnitsAfterAdd);
126
+
127
+ if (liquidityUnitsAfterAdd.getBaseValue("number") === 0) {
128
+ return estimatedLiquidityUnits.div(P).getBaseValue("number");
129
+ }
130
+
131
+ // get pool units after add
132
+ const newPoolUnits = P.add(estimatedLiquidityUnits);
133
+
134
+ return estimatedLiquidityUnits.div(newPoolUnits).getBaseValue("number");
135
+ }
136
+
137
+ export function getLiquiditySlippage({ runeAmount, assetAmount, runeDepth, assetDepth }: PoolParams) {
138
+ if (runeAmount === "0" || assetAmount === "0" || runeDepth === "0" || assetDepth === "0") return 0;
139
+ // formula: (t * R - T * r)/ (T*r + R*T)
140
+ const R = toTCSwapKitNumber(runeDepth);
141
+ const T = toTCSwapKitNumber(assetDepth);
142
+ const assetAddAmount = toTCSwapKitNumber(assetAmount);
143
+ const runeAddAmount = toTCSwapKitNumber(runeAmount);
144
+
145
+ const numerator = assetAddAmount.mul(R).sub(T.mul(runeAddAmount));
146
+ const denominator = T.mul(runeAddAmount).add(R.mul(T));
147
+
148
+ // set absolute value of percent, no negative allowed
149
+ return Math.abs(numerator.div(denominator).getBaseValue("number"));
150
+ }
@@ -0,0 +1,102 @@
1
+ import { Chain } from "@swapkit/types";
2
+ import { match } from "ts-pattern";
3
+ import { MemoType } from "../types/sdk";
4
+
5
+ export function getMemoForLeaveAndBond({ type, address }: BondOrLeaveParams) {
6
+ return `${type}:${address}`;
7
+ }
8
+
9
+ export function getMemoForUnbond({ address, unbondAmount }: UnbondParams) {
10
+ return `${MemoType.UNBOND}:${address}:${unbondAmount}`;
11
+ }
12
+
13
+ /**
14
+ * Deposit
15
+ */
16
+ export function getMemoForRunePoolDeposit(affiliate?: WithAffiliate<{}>) {
17
+ return addAffiliate(MemoType.RUNEPOOL_DEPOSIT, affiliate);
18
+ }
19
+
20
+ export function getMemoForDeposit({
21
+ chain,
22
+ symbol,
23
+ address,
24
+ ...affiliate
25
+ }: WithAffiliate<{ chain: Chain; symbol: string; address?: string }>) {
26
+ const poolIdentifier = getPoolIdentifier({ chain, symbol });
27
+ const addressPart = address ? `:${address}:` : ":";
28
+
29
+ return addAffiliate(`${MemoType.DEPOSIT}:${poolIdentifier}${addressPart}`, affiliate);
30
+ }
31
+
32
+ /**
33
+ * Withdraw
34
+ */
35
+ export function getMemoForWithdraw({ chain, symbol, ticker, basisPoints, targetAsset }: WithdrawParams) {
36
+ const shortenedSymbol = chain === "ETH" && ticker !== "ETH" ? `${ticker}-${symbol.slice(-3)}` : symbol;
37
+ const targetPart = targetAsset ? `:${targetAsset}` : "";
38
+
39
+ return `${MemoType.WITHDRAW}:${chain}.${shortenedSymbol}:${basisPoints}${targetPart}`;
40
+ }
41
+
42
+ export function getMemoForRunePoolWithdraw({ basisPoints, ...affiliate }: WithAffiliate<{ basisPoints: number }>) {
43
+ return addAffiliate(`${MemoType.RUNEPOOL_WITHDRAW}:${basisPoints}`, affiliate);
44
+ }
45
+
46
+ /**
47
+ * TNS
48
+ */
49
+ export function getMemoForNameRegister({ name, chain, address, owner }: NameRegisterParams) {
50
+ const baseMemo = `${MemoType.NAME_REGISTER}:${name}:${chain}:${address}`;
51
+ const ownerAssignmentOrChangePart = owner ? `:${owner}` : "";
52
+
53
+ return `${baseMemo}${ownerAssignmentOrChangePart}`;
54
+ }
55
+
56
+ export function getMemoForNamePreferredAssetRegister({
57
+ name,
58
+ chain,
59
+ asset,
60
+ payout,
61
+ owner,
62
+ }: PreferredAssetRegisterParams) {
63
+ return `${MemoType.NAME_REGISTER}:${name}:${chain}:${payout}:${owner}:${asset}`;
64
+ }
65
+
66
+ export function getMemoForTcyClaim(memoType: MemoType.CLAIM_TCY, { address }: WithAffiliate<{ address: string }>) {
67
+ return `${memoType}:${address}`;
68
+ }
69
+
70
+ export function getMemoForTcyStake(
71
+ memoType: MemoType.STAKE_TCY | MemoType.UNSTAKE_TCY,
72
+ { unstakeBps, ...affiliate }: WithAffiliate<{ unstakeBps?: number }>,
73
+ ) {
74
+ const bps = unstakeBps ? `:${unstakeBps}` : "";
75
+ const baseMemo = `${memoType}${bps}`;
76
+
77
+ return addAffiliate(`${baseMemo}`, affiliate);
78
+ }
79
+
80
+ /**
81
+ * Internal helpers
82
+ */
83
+ function addAffiliate(memo: string, { affiliateAddress, affiliateBasisPoints }: WithAffiliate<{}> = {}) {
84
+ const affiliatedMemo = `${memo}${affiliateAddress ? `:${affiliateAddress}:${affiliateBasisPoints || 0}` : ""}`;
85
+
86
+ return affiliatedMemo.endsWith(":") ? affiliatedMemo.slice(0, -1) : affiliatedMemo;
87
+ }
88
+
89
+ function getPoolIdentifier({ chain, symbol }: { chain: Chain; symbol: string }) {
90
+ return match(chain)
91
+ .with(Chain.Bitcoin, Chain.Dogecoin, Chain.Litecoin, () => chain.slice(0, 1).toLowerCase())
92
+ .with(Chain.BitcoinCash, () => "c")
93
+ .otherwise(() => `${chain}.${symbol}`);
94
+ }
95
+
96
+ type WithAffiliate<T extends {}> = T & { affiliateAddress?: string; affiliateBasisPoints?: number };
97
+
98
+ type BondOrLeaveParams = { type: MemoType.BOND | MemoType.LEAVE; address: string };
99
+ type UnbondParams = { address: string; unbondAmount: number };
100
+ type NameRegisterParams = { name: string; chain: string; address: string; owner?: string };
101
+ type PreferredAssetRegisterParams = { name: string; chain: Chain; asset: string; payout: string; owner: string };
102
+ type WithdrawParams = { chain: Chain; symbol: string; ticker: string; basisPoints: number; targetAsset?: string };
@@ -0,0 +1,62 @@
1
+ import { Chain } from "@swapkit/types";
2
+ import { type ErrorKeys, SwapKitError } from "../modules/swapKitError";
3
+
4
+ // 10 rune for register, 1 rune per year
5
+ // MINIMUM_REGISTRATION_FEE = 11
6
+ export function getTHORNameCost(numberOfYears: number) {
7
+ if (numberOfYears < 0)
8
+ throw new SwapKitError({ errorKey: "helpers_invalid_number_of_years", info: { numberOfYears } });
9
+ return 10 + numberOfYears;
10
+ }
11
+
12
+ // 10 CACAO for register
13
+ // 1.0512 CACAO per year
14
+ export function getMAYANameCost(numberOfYears: number) {
15
+ if (numberOfYears < 0)
16
+ throw new SwapKitError({ errorKey: "helpers_invalid_number_of_years", info: { numberOfYears } });
17
+ // round to max 10 decimals
18
+ return Math.round((10 + numberOfYears * 1.0512) * 1e10) / 1e10;
19
+ }
20
+
21
+ export function wrapWithThrow<T>(fn: () => T, errorKey?: ErrorKeys) {
22
+ try {
23
+ return fn();
24
+ } catch (error) {
25
+ if (errorKey) {
26
+ throw new SwapKitError(errorKey, error);
27
+ }
28
+
29
+ return;
30
+ }
31
+ }
32
+
33
+ export function getChainIdentifier<T extends Chain>(chain: T) {
34
+ switch (chain) {
35
+ case Chain.THORChain:
36
+ return `${chain}.RUNE`;
37
+
38
+ case Chain.Cosmos:
39
+ return `${chain}.ATOM`;
40
+
41
+ case Chain.BinanceSmartChain:
42
+ return `${chain}`;
43
+
44
+ default:
45
+ return `${chain}.${chain}`;
46
+ }
47
+ }
48
+
49
+ const warnings = new Set();
50
+ export function warnOnce({ condition, id, warning }: { condition: boolean; id: string; warning: string }) {
51
+ if (condition) {
52
+ if (warnings.has(id)) {
53
+ return;
54
+ }
55
+
56
+ if (process.env.NODE_ENV !== "test") {
57
+ console.warn(warning);
58
+ }
59
+
60
+ warnings.add(id);
61
+ }
62
+ }
@@ -0,0 +1,32 @@
1
+ import type { Chain } from "@swapkit/types";
2
+ import { SKConfig } from "../modules/swapKitConfig";
3
+ import { SwapKitError } from "../modules/swapKitError";
4
+
5
+ // Backward compatibility
6
+ const supportedChains = ["TERRA", ...SKConfig.get("chains")];
7
+
8
+ export function validateIdentifier(identifier = "") {
9
+ const uppercasedIdentifier = identifier.toUpperCase();
10
+
11
+ const [chain] = uppercasedIdentifier.split(".") as [Chain, string];
12
+ if (supportedChains.includes(chain)) return true;
13
+
14
+ const [synthChain] = uppercasedIdentifier.split("/") as [Chain, string];
15
+ if (supportedChains.includes(synthChain)) return true;
16
+
17
+ throw new SwapKitError({
18
+ errorKey: "helpers_invalid_identifier",
19
+ info: {
20
+ identifier,
21
+ message: `Invalid identifier: ${identifier}. Expected format: <Chain>.<Ticker> or <Chain>.<Ticker>-<ContractAddress>`,
22
+ },
23
+ });
24
+ }
25
+
26
+ export function validateTNS(name: string) {
27
+ if (name.length > 30) return false;
28
+
29
+ const regex = /^[a-zA-Z0-9+_-]+$/g;
30
+
31
+ return !!name.match(regex);
32
+ }