@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,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifications © 2025 Horizontal Systems.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TokenListName, TokenNames, TokenTax } from "@tcswap/tokens";
|
|
6
|
+
import { AllChains, Chain, type ChainId, type EVMChain, EVMChains, getChainConfig } from "@tcswap/types";
|
|
7
|
+
import { getAddress } from "ethers";
|
|
8
|
+
import { match } from "ts-pattern";
|
|
9
|
+
import {
|
|
10
|
+
assetFromString,
|
|
11
|
+
type CommonAssetString,
|
|
12
|
+
fetchTokenInfo,
|
|
13
|
+
getAssetType,
|
|
14
|
+
getCommonAssetInfo,
|
|
15
|
+
isGasAsset,
|
|
16
|
+
} from "../utils/asset";
|
|
17
|
+
import { warnOnce } from "../utils/others";
|
|
18
|
+
import { validateIdentifier } from "../utils/validators";
|
|
19
|
+
|
|
20
|
+
import type { NumberPrimitives } from "./bigIntArithmetics";
|
|
21
|
+
import { BigIntArithmetics, formatBigIntToSafeValue } from "./bigIntArithmetics";
|
|
22
|
+
import { USwapError } from "./uSwapError";
|
|
23
|
+
import type { SwapKitValueType } from "./uSwapNumber";
|
|
24
|
+
|
|
25
|
+
const CASE_SENSITIVE_CHAINS: Chain[] = [Chain.Solana, Chain.Tron, Chain.Near, Chain.Sui];
|
|
26
|
+
const TC_CHAINS: Chain[] = [Chain.THORChain, Chain.Maya];
|
|
27
|
+
|
|
28
|
+
const staticTokensMap = new Map<
|
|
29
|
+
TokenNames | (string & {}),
|
|
30
|
+
{ tax?: TokenTax; decimal: number; identifier: string; logoURI?: string }
|
|
31
|
+
>();
|
|
32
|
+
|
|
33
|
+
const chainAddressIdentifierMap = new Map<string, string>();
|
|
34
|
+
|
|
35
|
+
const asyncTokenCache = new Map<string, { identifier: string; decimals: number; timestamp: number }>();
|
|
36
|
+
const CACHE_TTL = 3600000;
|
|
37
|
+
|
|
38
|
+
function getCachedTokenInfo(key: string) {
|
|
39
|
+
const cached = asyncTokenCache.get(key);
|
|
40
|
+
|
|
41
|
+
if (cached?.timestamp && Date.now() - cached.timestamp > CACHE_TTL) {
|
|
42
|
+
asyncTokenCache.delete(key);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function setCachedTokenInfo(key: string, info: { identifier: string; decimals: number }) {
|
|
50
|
+
if (asyncTokenCache.size > 1000) {
|
|
51
|
+
const firstKey = asyncTokenCache.keys().next().value;
|
|
52
|
+
if (firstKey) asyncTokenCache.delete(firstKey);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
asyncTokenCache.set(key, { ...info, timestamp: Date.now() });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type ConditionalAssetValueReturn<T extends { asyncTokenLookup?: boolean }> = T["asyncTokenLookup"] extends true
|
|
59
|
+
? Promise<AssetValue>
|
|
60
|
+
: AssetValue;
|
|
61
|
+
|
|
62
|
+
type AssetIdentifier =
|
|
63
|
+
| { asset: CommonAssetString | TokenNames }
|
|
64
|
+
| { asset: string }
|
|
65
|
+
| { chain: Chain; address?: string };
|
|
66
|
+
|
|
67
|
+
type AssetValueFromParams = AssetIdentifier & {
|
|
68
|
+
value?: NumberPrimitives | SwapKitValueType;
|
|
69
|
+
fromBaseDecimal?: number;
|
|
70
|
+
asyncTokenLookup?: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export class AssetValue extends BigIntArithmetics {
|
|
74
|
+
address?: string;
|
|
75
|
+
chain: Chain;
|
|
76
|
+
isGasAsset = false;
|
|
77
|
+
isSynthetic = false;
|
|
78
|
+
isTradeAsset = false;
|
|
79
|
+
symbol: string;
|
|
80
|
+
tax?: TokenTax;
|
|
81
|
+
ticker: string;
|
|
82
|
+
type: ReturnType<typeof getAssetType>;
|
|
83
|
+
chainId: ChainId;
|
|
84
|
+
|
|
85
|
+
constructor({
|
|
86
|
+
value,
|
|
87
|
+
decimal,
|
|
88
|
+
tax,
|
|
89
|
+
chain,
|
|
90
|
+
symbol,
|
|
91
|
+
identifier,
|
|
92
|
+
}: { decimal: number; value: SwapKitValueType; tax?: TokenTax } & (
|
|
93
|
+
| { chain: Chain; symbol: string; identifier?: never }
|
|
94
|
+
| { identifier: string; chain?: never; symbol?: never }
|
|
95
|
+
)) {
|
|
96
|
+
super(typeof value === "object" ? value : { decimal, value });
|
|
97
|
+
|
|
98
|
+
const assetInfo = getAssetInfo(identifier || `${chain}.${symbol}`);
|
|
99
|
+
|
|
100
|
+
this.type = getAssetType(assetInfo);
|
|
101
|
+
this.tax = tax;
|
|
102
|
+
this.chain = assetInfo.chain;
|
|
103
|
+
this.ticker = assetInfo.ticker;
|
|
104
|
+
this.symbol = assetInfo.symbol;
|
|
105
|
+
this.address = assetInfo.address;
|
|
106
|
+
this.isSynthetic = assetInfo.isSynthetic;
|
|
107
|
+
this.isTradeAsset = assetInfo.isTradeAsset;
|
|
108
|
+
this.isGasAsset = assetInfo.isGasAsset;
|
|
109
|
+
this.chainId = getChainConfig(assetInfo.chain).chainId;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
toString({ includeSynthProtocol }: { includeSynthProtocol?: boolean } = {}) {
|
|
113
|
+
return (this.isSynthetic || this.isTradeAsset) && !includeSynthProtocol
|
|
114
|
+
? this.symbol
|
|
115
|
+
: `${this.chain}.${this.symbol}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
toUrl() {
|
|
119
|
+
if (this.isSynthetic) {
|
|
120
|
+
return `${this.chain}.${this.symbol.replace(/\//g, ".")}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this.isTradeAsset) {
|
|
124
|
+
return `${this.chain}.${this.symbol.replace(/~/g, "..")}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const encodedSymbol = this.symbol.replace(/\./g, "__");
|
|
128
|
+
return `${this.chain}.${encodedSymbol}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getIconUrl() {
|
|
132
|
+
const token = staticTokensMap.get(this.toString());
|
|
133
|
+
return token?.logoURI;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
eqAsset({ chain, symbol }: { chain: Chain; symbol: string }) {
|
|
137
|
+
return this.chain === chain && this.symbol === symbol;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
eq(assetValue: AssetValue) {
|
|
141
|
+
return this.eqAsset(assetValue) && this.eqValue(assetValue);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static fromUrl(urlAsset: string, value: NumberPrimitives = 0) {
|
|
145
|
+
const firstDotIndex = urlAsset.indexOf(".");
|
|
146
|
+
|
|
147
|
+
if (firstDotIndex === -1) {
|
|
148
|
+
throw new USwapError({ errorKey: "helpers_invalid_asset_url", info: { urlAsset } });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const chain = urlAsset.slice(0, firstDotIndex);
|
|
152
|
+
const rest = urlAsset.slice(firstDotIndex + 1);
|
|
153
|
+
|
|
154
|
+
const asset = match({ chain: chain as Chain, rest })
|
|
155
|
+
.when(
|
|
156
|
+
({ rest }) => rest.includes(".."),
|
|
157
|
+
({ chain, rest }) => `${chain}.${rest.replace(/\.\./g, "~")}`,
|
|
158
|
+
)
|
|
159
|
+
.when(
|
|
160
|
+
({ chain, rest }) => TC_CHAINS.includes(chain) && rest.includes("."),
|
|
161
|
+
({ chain, rest }) => `${chain}.${rest.replace(/\./g, "/")}`,
|
|
162
|
+
)
|
|
163
|
+
.otherwise(({ chain, rest }) => `${chain}.${rest.replace(/__/g, ".")}`);
|
|
164
|
+
|
|
165
|
+
return AssetValue.from({ asset, value });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static from<T extends {}>({
|
|
169
|
+
value = 0,
|
|
170
|
+
fromBaseDecimal,
|
|
171
|
+
asyncTokenLookup,
|
|
172
|
+
...fromAssetOrChain
|
|
173
|
+
}: T & AssetValueFromParams): ConditionalAssetValueReturn<T> {
|
|
174
|
+
const parsedValue = value instanceof BigIntArithmetics ? value.getValue("string") : value;
|
|
175
|
+
const assetOrChain = getAssetString(fromAssetOrChain);
|
|
176
|
+
|
|
177
|
+
const isChainAddressCombo = !assetOrChain.startsWith(Chain.Sui) && assetOrChain.includes(":");
|
|
178
|
+
|
|
179
|
+
if (asyncTokenLookup && isChainAddressCombo) {
|
|
180
|
+
const [chain, address] = assetOrChain.split(":") as [Chain, string];
|
|
181
|
+
|
|
182
|
+
return createAsyncAssetValue({ address, chain, fromBaseDecimal, parsedValue }) as ConditionalAssetValueReturn<T>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const fallbackIdentifier = isChainAddressCombo ? assetOrChain.split(":").join(".UNKNOWN-") : assetOrChain;
|
|
186
|
+
|
|
187
|
+
const { identifier: unsafeIdentifier, decimal: commonAssetDecimal } = getCommonAssetInfo(
|
|
188
|
+
fallbackIdentifier as CommonAssetString,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const { chain, isSynthetic, isTradeAsset, address } = getAssetInfo(unsafeIdentifier);
|
|
192
|
+
const { baseDecimal } = getChainConfig(chain);
|
|
193
|
+
|
|
194
|
+
const token = staticTokensMap.get(
|
|
195
|
+
CASE_SENSITIVE_CHAINS.includes(chain)
|
|
196
|
+
? (unsafeIdentifier as TokenNames)
|
|
197
|
+
: (unsafeIdentifier.toUpperCase() as TokenNames),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!token && asyncTokenLookup && !isSynthetic && !isTradeAsset) {
|
|
201
|
+
return (async () => {
|
|
202
|
+
const { ticker } = assetFromString(unsafeIdentifier);
|
|
203
|
+
const tokenData = await fetchTokenData({ address, chain, ticker });
|
|
204
|
+
return createAssetValue({
|
|
205
|
+
decimal: tokenData.decimals,
|
|
206
|
+
identifier: tokenData.identifier,
|
|
207
|
+
value: fromBaseDecimal ? safeValue(BigInt(parsedValue), fromBaseDecimal) : parsedValue,
|
|
208
|
+
});
|
|
209
|
+
})() as ConditionalAssetValueReturn<T>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const tokenDecimal = token?.decimal || commonAssetDecimal;
|
|
213
|
+
|
|
214
|
+
warnOnce({
|
|
215
|
+
condition: !tokenDecimal && !asyncTokenLookup,
|
|
216
|
+
id: `assetValue_static_decimal_not_found_${chain}`,
|
|
217
|
+
warning: `Couldn't find static decimal for one or more tokens on ${chain} (Using default ${baseDecimal} decimal as fallback).
|
|
218
|
+
This can result in incorrect calculations and mess with amount sent on transactions.
|
|
219
|
+
You can load static assets by installing @tcswap/tokens package and calling AssetValue.loadStaticAssets()
|
|
220
|
+
or by passing asyncTokenLookup: true to the from() function, which will make it async and return a promise.`,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const { decimal, identifier, tax } = token || {
|
|
224
|
+
decimal: tokenDecimal || baseDecimal,
|
|
225
|
+
identifier: unsafeIdentifier,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const adjustedValue = fromBaseDecimal
|
|
229
|
+
? safeValue(BigInt(parsedValue), fromBaseDecimal)
|
|
230
|
+
: safeValue(parsedValue, decimal);
|
|
231
|
+
|
|
232
|
+
const assetValue =
|
|
233
|
+
isSynthetic || isTradeAsset
|
|
234
|
+
? createSyntheticAssetValue(identifier, adjustedValue)
|
|
235
|
+
: createAssetValue({ decimal, identifier, tax, value: adjustedValue });
|
|
236
|
+
|
|
237
|
+
return assetValue as ConditionalAssetValueReturn<T>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static async loadStaticAssets(listNames?: TokenListName[]) {
|
|
241
|
+
const { loadTokenLists } = await import("@tcswap/tokens");
|
|
242
|
+
const lists = await loadTokenLists(listNames);
|
|
243
|
+
|
|
244
|
+
for (const { tokens } of Object.values(lists)) {
|
|
245
|
+
for (const { identifier, chain, ...rest } of tokens) {
|
|
246
|
+
const chainConfig = getChainConfig(chain as Chain);
|
|
247
|
+
|
|
248
|
+
const tokenKey = (
|
|
249
|
+
CASE_SENSITIVE_CHAINS.includes(chainConfig.chain) ? identifier : identifier.toUpperCase()
|
|
250
|
+
) as TokenNames;
|
|
251
|
+
const tokenDecimal = "decimals" in rest ? rest.decimals : chainConfig.baseDecimal;
|
|
252
|
+
|
|
253
|
+
const tokenInfo = {
|
|
254
|
+
decimal: tokenDecimal,
|
|
255
|
+
identifier,
|
|
256
|
+
logoURI: "logoURI" in rest ? (rest.logoURI as string) : undefined,
|
|
257
|
+
tax: "tax" in rest ? (rest.tax as TokenTax) : undefined,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
staticTokensMap.set(tokenKey, tokenInfo);
|
|
261
|
+
|
|
262
|
+
// Also populate chain:address map for quick lookups
|
|
263
|
+
if ("address" in rest && rest.address) {
|
|
264
|
+
const lookupKey = CASE_SENSITIVE_CHAINS.includes(chainConfig.chain)
|
|
265
|
+
? `${chainConfig.chain}:${rest.address}`
|
|
266
|
+
: `${chainConfig.chain}:${rest.address.toUpperCase()}`;
|
|
267
|
+
chainAddressIdentifierMap.set(lookupKey, identifier);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
static setStaticAssets(
|
|
276
|
+
tokenMap: Map<
|
|
277
|
+
string,
|
|
278
|
+
{ tax?: TokenTax; identifier: string; chain: Chain; address?: string } & (
|
|
279
|
+
| { decimal: number }
|
|
280
|
+
| { decimals: number }
|
|
281
|
+
)
|
|
282
|
+
>,
|
|
283
|
+
) {
|
|
284
|
+
staticTokensMap.clear();
|
|
285
|
+
chainAddressIdentifierMap.clear();
|
|
286
|
+
|
|
287
|
+
for (const [key, value] of tokenMap.entries()) {
|
|
288
|
+
const tokenKey = (
|
|
289
|
+
CASE_SENSITIVE_CHAINS.includes(value.chain) ? value.identifier : value.identifier.toUpperCase()
|
|
290
|
+
) as TokenNames;
|
|
291
|
+
const tokenDecimal = "decimals" in value ? value.decimals : value.decimal;
|
|
292
|
+
const tokenInfo = { ...value, decimal: tokenDecimal, identifier: tokenKey };
|
|
293
|
+
|
|
294
|
+
staticTokensMap.set(key, tokenInfo);
|
|
295
|
+
|
|
296
|
+
if (value.address) {
|
|
297
|
+
const lookupKey = CASE_SENSITIVE_CHAINS.includes(value.chain)
|
|
298
|
+
? `${value.chain}:${value.address}`
|
|
299
|
+
: `${value.chain}:${value.address.toUpperCase()}`;
|
|
300
|
+
chainAddressIdentifierMap.set(lookupKey, value.identifier);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static get staticAssets() {
|
|
307
|
+
return staticTokensMap;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function getMinAmountByChain(chain: Chain) {
|
|
312
|
+
const asset = AssetValue.from({ chain });
|
|
313
|
+
|
|
314
|
+
return match(chain)
|
|
315
|
+
.with(Chain.Bitcoin, Chain.Litecoin, Chain.BitcoinCash, Chain.Dash, () => asset.set(0.00010001))
|
|
316
|
+
.with(Chain.Dogecoin, () => asset.set(1.00000001))
|
|
317
|
+
.with(Chain.Avalanche, Chain.Ethereum, Chain.Arbitrum, Chain.BinanceSmartChain, () => asset.set(0.00000001))
|
|
318
|
+
.with(Chain.THORChain, Chain.Maya, () => asset.set(0))
|
|
319
|
+
.with(Chain.Cosmos, Chain.Kujira, () => asset.set(0.000001))
|
|
320
|
+
.otherwise(() => asset.set(0.00000001));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function fetchTokenData({ chain, address, ticker }: { chain: Chain; address?: string; ticker?: string }) {
|
|
324
|
+
const isCaseSensitiveChain = CASE_SENSITIVE_CHAINS.includes(chain);
|
|
325
|
+
|
|
326
|
+
const cacheKey = isCaseSensitiveChain
|
|
327
|
+
? `${chain}:${address || ticker}`
|
|
328
|
+
: `${chain}:${address || ticker}`.toUpperCase();
|
|
329
|
+
|
|
330
|
+
const cached = getCachedTokenInfo(cacheKey);
|
|
331
|
+
if (cached) {
|
|
332
|
+
return cached;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!address) {
|
|
336
|
+
const { baseDecimal } = getChainConfig(chain);
|
|
337
|
+
return { decimals: baseDecimal, identifier: `${chain}.${ticker || "UNKNOWN"}` };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const tokenInfo = await fetchTokenInfo({ address, chain });
|
|
341
|
+
|
|
342
|
+
const identifier = `${chain}.${tokenInfo.ticker || ticker || "UNKNOWN"}-${address}`;
|
|
343
|
+
|
|
344
|
+
warnOnce({
|
|
345
|
+
condition: !!(!tokenInfo.ticker && ticker),
|
|
346
|
+
id: `async_token_lookup_failed_${chain}_${address}`,
|
|
347
|
+
warning: `Could not fetch token metadata for ${chain}:${address} from chain. Using user-provided ticker (${ticker}) with baseDecimal (${tokenInfo.decimals}).`,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// only cache if we got a proper ticker back
|
|
351
|
+
tokenInfo.ticker && setCachedTokenInfo(cacheKey, { decimals: tokenInfo.decimals, identifier });
|
|
352
|
+
|
|
353
|
+
return { decimals: tokenInfo.decimals, identifier };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function createAssetValue({
|
|
357
|
+
identifier,
|
|
358
|
+
decimal,
|
|
359
|
+
value,
|
|
360
|
+
tax,
|
|
361
|
+
}: {
|
|
362
|
+
identifier: string;
|
|
363
|
+
decimal: number;
|
|
364
|
+
value: NumberPrimitives;
|
|
365
|
+
tax?: TokenTax;
|
|
366
|
+
}) {
|
|
367
|
+
validateIdentifier(identifier);
|
|
368
|
+
return new AssetValue({ decimal, identifier, tax, value: safeValue(value, decimal) });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function createSyntheticAssetValue(identifier: string, value: NumberPrimitives = 0) {
|
|
372
|
+
const chain = identifier.includes(".") ? (identifier.split(".")?.[0]?.toUpperCase() as Chain) : undefined;
|
|
373
|
+
const isMayaOrThor = chain ? TC_CHAINS.includes(chain) : false;
|
|
374
|
+
|
|
375
|
+
const assetSeparator = identifier.slice(0, 14).includes("~") ? "~" : "/";
|
|
376
|
+
|
|
377
|
+
const [synthChain, symbol] = isMayaOrThor
|
|
378
|
+
? identifier.split(".").slice(1).join().split(assetSeparator)
|
|
379
|
+
: identifier.split(assetSeparator);
|
|
380
|
+
|
|
381
|
+
if (!(synthChain && symbol)) {
|
|
382
|
+
throw new USwapError({ errorKey: "helpers_invalid_asset_identifier", info: { identifier } });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return new AssetValue({
|
|
386
|
+
decimal: 8,
|
|
387
|
+
identifier: `${chain || Chain.THORChain}.${synthChain}${assetSeparator}${symbol}`,
|
|
388
|
+
value: safeValue(value, 8),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function createAsyncAssetValue({
|
|
393
|
+
address,
|
|
394
|
+
chain,
|
|
395
|
+
fromBaseDecimal,
|
|
396
|
+
parsedValue,
|
|
397
|
+
}: {
|
|
398
|
+
address: string;
|
|
399
|
+
chain: Chain;
|
|
400
|
+
fromBaseDecimal?: number;
|
|
401
|
+
parsedValue: NumberPrimitives;
|
|
402
|
+
}): Promise<AssetValue> {
|
|
403
|
+
const { decimals, identifier } = await fetchTokenData({ address, chain });
|
|
404
|
+
const value = fromBaseDecimal ? safeValue(BigInt(parsedValue), fromBaseDecimal) : parsedValue;
|
|
405
|
+
return createAssetValue({ decimal: decimals, identifier, value });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function safeValue(value: NumberPrimitives, decimal: number) {
|
|
409
|
+
return typeof value === "bigint" ? formatBigIntToSafeValue({ bigIntDecimal: decimal, decimal, value }) : value;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function validateAssetChain(assetOrChain: AssetIdentifier) {
|
|
413
|
+
const chain = match(assetOrChain)
|
|
414
|
+
.when(
|
|
415
|
+
(x): x is { chain: Chain } => "chain" in x && x.chain !== undefined,
|
|
416
|
+
({ chain }) => chain,
|
|
417
|
+
)
|
|
418
|
+
.otherwise((x) => {
|
|
419
|
+
const assetInfo = assetFromString((x as { asset: string }).asset);
|
|
420
|
+
return assetInfo.synth ? Chain.THORChain : assetInfo.chain;
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// TODO: move to USwapConfig chains once we support it throughout sdk
|
|
424
|
+
if (!AllChains.includes(chain.toUpperCase() as Chain)) {
|
|
425
|
+
throw new USwapError({
|
|
426
|
+
errorKey: "helpers_invalid_asset_identifier",
|
|
427
|
+
info: { message: "Please use the AssetValue constructor for unsupported chains" },
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function getAssetString(assetOrChain: AssetIdentifier) {
|
|
433
|
+
validateAssetChain(assetOrChain);
|
|
434
|
+
|
|
435
|
+
if ("chain" in assetOrChain) {
|
|
436
|
+
const { chain, address } = assetOrChain;
|
|
437
|
+
|
|
438
|
+
if (address) {
|
|
439
|
+
const lookupKey = CASE_SENSITIVE_CHAINS.includes(chain as Chain)
|
|
440
|
+
? `${chain}:${address}`
|
|
441
|
+
: `${chain}:${address.toUpperCase()}`;
|
|
442
|
+
const identifier = chainAddressIdentifierMap.get(lookupKey);
|
|
443
|
+
if (identifier) return identifier;
|
|
444
|
+
return lookupKey;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return chain;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const { chain, symbol } = assetFromString(assetOrChain.asset);
|
|
451
|
+
const isNativeChain = getAssetType({ chain, symbol }) === "Native";
|
|
452
|
+
|
|
453
|
+
return isNativeChain ? chain : assetOrChain.asset;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function getSyntheticOrTradeAssetInfo(identifier: string, isSynthetic: boolean, isTradeAsset: boolean) {
|
|
457
|
+
const splitIdentifier = identifier.split(".");
|
|
458
|
+
const identifierChain = splitIdentifier[0]?.toUpperCase() as Chain;
|
|
459
|
+
const isThorOrMaya = TC_CHAINS.includes(identifierChain);
|
|
460
|
+
|
|
461
|
+
const assetSeparator = isTradeAsset ? "~" : "/";
|
|
462
|
+
|
|
463
|
+
const [synthChain, synthSymbol = ""] = isThorOrMaya
|
|
464
|
+
? splitIdentifier.slice(1).join(".").split(assetSeparator)
|
|
465
|
+
: identifier.split(assetSeparator);
|
|
466
|
+
|
|
467
|
+
if (!(synthChain && synthSymbol)) {
|
|
468
|
+
throw new USwapError({ errorKey: "helpers_invalid_asset_identifier", info: { identifier } });
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Get the ticker from the base symbol (e.g., "AVAX" from "AVAX/AVAX")
|
|
472
|
+
const { ticker, address } = getAssetBaseInfo({ chain: synthChain as Chain, symbol: synthSymbol });
|
|
473
|
+
const finalSymbol = `${synthChain}${assetSeparator}${synthSymbol}`;
|
|
474
|
+
|
|
475
|
+
return { address, chain: identifierChain, isGasAsset: false, isSynthetic, isTradeAsset, symbol: finalSymbol, ticker };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function getNormalAssetInfo(identifier: string) {
|
|
479
|
+
const firstDotIndex = identifier.indexOf(".");
|
|
480
|
+
const chain = (firstDotIndex === -1 ? identifier : identifier.slice(0, firstDotIndex)).toUpperCase() as Chain;
|
|
481
|
+
const assetSymbol = firstDotIndex === -1 ? identifier : identifier.slice(firstDotIndex + 1);
|
|
482
|
+
|
|
483
|
+
const { address, ticker } = getAssetBaseInfo({ chain, symbol: assetSymbol });
|
|
484
|
+
|
|
485
|
+
let formattedAddress: string | undefined;
|
|
486
|
+
try {
|
|
487
|
+
formattedAddress =
|
|
488
|
+
address && EVMChains.includes(chain as EVMChain) && getAddress(address) ? getAddress(address) : address;
|
|
489
|
+
} catch {
|
|
490
|
+
formattedAddress = address;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const finalSymbol = formattedAddress ? `${ticker}-${formattedAddress}` : assetSymbol;
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
address: formattedAddress,
|
|
497
|
+
chain,
|
|
498
|
+
isGasAsset: isGasAsset({ chain, symbol: assetSymbol }),
|
|
499
|
+
isSynthetic: false,
|
|
500
|
+
isTradeAsset: false,
|
|
501
|
+
symbol: finalSymbol,
|
|
502
|
+
ticker,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function getAssetInfo(identifier: string) {
|
|
507
|
+
const shortIdentifier = identifier.slice(0, 14);
|
|
508
|
+
const isSynthetic = shortIdentifier.includes("/");
|
|
509
|
+
const isTradeAsset = shortIdentifier.includes("~");
|
|
510
|
+
|
|
511
|
+
if (isSynthetic || isTradeAsset) {
|
|
512
|
+
return getSyntheticOrTradeAssetInfo(identifier, isSynthetic, isTradeAsset);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return getNormalAssetInfo(identifier);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function parseSymbolWithSeparator(symbol: string, useFirst = false) {
|
|
519
|
+
const dashIndex = useFirst ? symbol.indexOf("-") : symbol.lastIndexOf("-");
|
|
520
|
+
|
|
521
|
+
if (dashIndex === -1) {
|
|
522
|
+
return { address: undefined, ticker: symbol };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const ticker = symbol.slice(0, dashIndex);
|
|
526
|
+
const address = symbol.slice(dashIndex + 1);
|
|
527
|
+
return { address, ticker };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function getAssetBaseInfo({ symbol, chain }: { symbol: string; chain: Chain }) {
|
|
531
|
+
const { ticker, address } = parseSymbolWithSeparator(symbol, chain === Chain.Near);
|
|
532
|
+
|
|
533
|
+
const finalAddress = address && !CASE_SENSITIVE_CHAINS.includes(chain) ? address.toLowerCase() : address;
|
|
534
|
+
|
|
535
|
+
return { address: finalAddress, ticker };
|
|
536
|
+
}
|