@swapkit/core 1.0.0-rc.12 → 1.0.0-rc.121

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 (45) hide show
  1. package/dist/index.js +7324 -0
  2. package/dist/index.js.map +41 -0
  3. package/package.json +33 -47
  4. package/src/__tests__/helpers.test.ts +53 -0
  5. package/src/aggregator/contracts/avaxGeneric.ts +50 -50
  6. package/src/aggregator/contracts/avaxWoofi.ts +80 -80
  7. package/src/aggregator/contracts/bscGeneric.ts +59 -59
  8. package/src/aggregator/contracts/ethGeneric.ts +50 -50
  9. package/src/aggregator/contracts/index.ts +30 -28
  10. package/src/aggregator/contracts/pancakeV2.ts +80 -80
  11. package/src/aggregator/contracts/pangolin.ts +65 -65
  12. package/src/aggregator/contracts/routers/index.ts +58 -0
  13. package/src/aggregator/contracts/routers/kyber.ts +402 -0
  14. package/src/aggregator/contracts/routers/oneinch.ts +2188 -0
  15. package/src/aggregator/contracts/routers/pancakeswap.ts +340 -0
  16. package/src/aggregator/contracts/routers/pangolin.ts +340 -0
  17. package/src/aggregator/contracts/routers/sushiswap.ts +340 -0
  18. package/src/aggregator/contracts/routers/traderJoe.ts +340 -0
  19. package/src/aggregator/contracts/routers/uniswapv2.ts +340 -0
  20. package/src/aggregator/contracts/routers/uniswapv3.ts +254 -0
  21. package/src/aggregator/contracts/routers/woofi.ts +171 -0
  22. package/src/aggregator/contracts/sushiswap.ts +65 -65
  23. package/src/aggregator/contracts/traderJoe.ts +65 -65
  24. package/src/aggregator/contracts/uniswapV2.ts +65 -65
  25. package/src/aggregator/contracts/uniswapV2Leg.ts +70 -70
  26. package/src/aggregator/contracts/uniswapV3_100.ts +70 -70
  27. package/src/aggregator/contracts/uniswapV3_10000.ts +70 -70
  28. package/src/aggregator/contracts/uniswapV3_3000.ts +70 -70
  29. package/src/aggregator/contracts/uniswapV3_500.ts +70 -70
  30. package/src/aggregator/getSwapParams.ts +12 -12
  31. package/src/client/index.ts +206 -646
  32. package/src/client/old.ts +854 -0
  33. package/src/helpers/explorerUrls.ts +38 -0
  34. package/src/index.ts +6 -4
  35. package/src/types.ts +149 -0
  36. package/LICENSE +0 -201
  37. package/dist/index-9e36735e.cjs +0 -1
  38. package/dist/index-cf1865cd.js +0 -649
  39. package/dist/index.cjs +0 -2
  40. package/dist/index.d.ts +0 -216
  41. package/dist/index.es.js +0 -3818
  42. package/src/client/__tests__/helpers.test.ts +0 -69
  43. package/src/client/explorerUrls.ts +0 -62
  44. package/src/client/thornode.ts +0 -31
  45. package/src/client/types.ts +0 -103
@@ -1,689 +1,249 @@
1
- import type { Keys, ThornameRegisterParam } from '@swapkit/helpers';
1
+ import { AssetValue, SwapKitError } from "@swapkit/helpers";
2
+ import { Chain } from "@swapkit/types";
2
3
  import {
3
- AssetValue,
4
- gasFeeMultiplier,
5
- getMemoFor,
6
- getMinAmountByChain,
7
- SwapKitError,
8
- SwapKitNumber,
9
- } from '@swapkit/helpers';
10
- import type { CosmosLikeToolbox } from '@swapkit/toolbox-cosmos';
11
- import type { AVAXToolbox, BSCToolbox, ETHToolbox, EVMToolbox } from '@swapkit/toolbox-evm';
12
- import type { UTXOToolbox } from '@swapkit/toolbox-utxo';
4
+ getExplorerAddressUrl as getAddressUrl,
5
+ getExplorerTxUrl as getTxUrl,
6
+ } from "../helpers/explorerUrls.ts";
13
7
  import type {
14
- AddChainWalletParams,
15
- EVMChain,
16
- EVMWalletOptions,
17
- ExtendParams,
18
- WalletOption,
19
- } from '@swapkit/types';
20
- import {
21
- AGG_SWAP,
22
- Chain,
23
- ChainToChainId,
24
- FeeOption,
25
- MemoType,
26
- SWAP_IN,
27
- SWAP_OUT,
28
- TCAvalancheDepositABI,
29
- TCBscDepositABI,
30
- TCEthereumVaultAbi,
31
- } from '@swapkit/types';
32
-
33
- import type { AGG_CONTRACT_ADDRESS } from '../aggregator/contracts/index.ts';
34
- import { lowercasedContractAbiMapping } from '../aggregator/contracts/index.ts';
35
- import { getSwapInParams } from '../aggregator/getSwapParams.ts';
36
-
37
- import { getExplorerAddressUrl, getExplorerTxUrl } from './explorerUrls.ts';
38
- import { getInboundData, getMimirData } from './thornode.ts';
39
- import type {
40
- CoreTxParams,
41
- EVMWallet,
42
- SwapParams,
43
- ThorchainWallet,
44
- Wallet,
45
- WalletMethods,
46
- } from './types.ts';
47
-
48
- const getEmptyWalletStructure = () =>
49
- (Object.values(Chain) as Chain[]).reduce(
50
- (acc, chain) => {
51
- acc[chain] = null;
52
- return acc;
53
- },
54
- {} as Record<Chain, null>,
55
- );
56
-
57
- export class SwapKitCore<T = ''> {
58
- public connectedChains: Wallet = getEmptyWalletStructure();
59
- public connectedWallets: WalletMethods = getEmptyWalletStructure();
60
- public readonly stagenet: boolean = false;
61
-
62
- constructor({ stagenet }: { stagenet?: boolean } | undefined = {}) {
63
- this.stagenet = !!stagenet;
64
- }
65
-
66
- getAddress = (chain: Chain) => this.connectedChains[chain]?.address || '';
67
- getExplorerTxUrl = (chain: Chain, txHash: string) => getExplorerTxUrl({ chain, txHash });
68
- getWallet = <T extends Chain>(chain: Chain) => this.connectedWallets[chain] as WalletMethods[T];
69
- getExplorerAddressUrl = (chain: Chain, address: string) =>
70
- getExplorerAddressUrl({ chain, address });
71
- getBalance = async (chain: Chain, potentialScamFilter?: boolean) => {
72
- const wallet = await this.getWalletByChain(chain, potentialScamFilter);
73
-
74
- return wallet?.balance || [];
75
- };
76
-
77
- swap = async ({ streamSwap, recipient, route, feeOptionKey }: SwapParams) => {
78
- const { quoteMode } = route.meta;
79
- const evmChain = quoteMode.startsWith('ERC20-')
80
- ? Chain.Ethereum
81
- : quoteMode.startsWith('ARC20-')
82
- ? Chain.Avalanche
83
- : Chain.BinanceSmartChain;
84
-
85
- if (!route.complete) throw new SwapKitError('core_swap_route_not_complete');
86
-
87
- try {
88
- if (AGG_SWAP.includes(quoteMode)) {
89
- const walletMethods = this.connectedWallets[evmChain];
90
- if (!walletMethods?.sendTransaction) {
91
- throw new SwapKitError('core_wallet_connection_not_found');
92
- }
93
-
94
- const transaction = streamSwap ? route?.streamingSwap?.transaction : route?.transaction;
95
- if (!transaction) throw new SwapKitError('core_swap_route_transaction_not_found');
96
-
97
- const { isHexString, parseUnits } = await import('ethers');
98
- const { data, from, to, value } = route.transaction;
99
-
100
- const params = {
101
- data,
102
- from,
103
- to: to.toLowerCase(),
104
- chainId: BigInt(ChainToChainId[evmChain]),
105
- value: value
106
- ? new SwapKitNumber({
107
- value: !isHexString(value) ? parseUnits(value, 'wei').toString(16) : value,
108
- }).baseValueBigInt
109
- : 0n,
110
- };
111
-
112
- return walletMethods.sendTransaction(params, feeOptionKey) as Promise<string>;
113
- }
114
-
115
- if (SWAP_OUT.includes(quoteMode)) {
116
- if (!route.calldata.fromAsset) throw new SwapKitError('core_swap_asset_not_recognized');
117
- const asset = await AssetValue.fromString(route.calldata.fromAsset);
118
- if (!asset) throw new SwapKitError('core_swap_asset_not_recognized');
119
-
120
- const { address: recipient } = await this.#getInboundDataByChain(asset.chain);
121
- const {
122
- contract: router,
123
- calldata: { amountIn, memo, memoStreamingSwap },
124
- } = route;
125
-
126
- const assetValue = asset.add(SwapKitNumber.fromBigInt(BigInt(amountIn), asset.decimal));
127
- const swapMemo = (streamSwap ? memoStreamingSwap || memo : memo) as string;
128
-
129
- return this.deposit({ assetValue, memo: swapMemo, feeOptionKey, router, recipient });
130
- }
131
-
132
- if (SWAP_IN.includes(quoteMode)) {
133
- const { calldata, contract: contractAddress } = route;
134
- if (!contractAddress) throw new SwapKitError('core_swap_contract_not_found');
135
-
136
- const walletMethods = this.connectedWallets[evmChain];
137
- const from = this.getAddress(evmChain);
138
- if (!walletMethods?.sendTransaction || !from) {
139
- throw new SwapKitError('core_wallet_connection_not_found');
140
- }
141
-
142
- const { getProvider, toChecksumAddress } = await import('@swapkit/toolbox-evm');
143
- const provider = getProvider(evmChain);
144
- const abi = lowercasedContractAbiMapping[contractAddress.toLowerCase()];
145
-
146
- if (!abi) throw new SwapKitError('core_swap_contract_not_supported', { contractAddress });
147
-
148
- const contract = await walletMethods.createContract?.(contractAddress, abi, provider);
149
-
150
- // TODO: (@Towan) Contract evm methods should be generic
151
- // @ts-expect-error
152
- const tx = await contract.populateTransaction.swapIn?.(
153
- ...getSwapInParams({
154
- streamSwap,
155
- toChecksumAddress,
156
- contractAddress: contractAddress as AGG_CONTRACT_ADDRESS,
157
- recipient,
158
- calldata,
159
- }),
160
- { from },
161
- );
162
-
163
- return walletMethods.sendTransaction(tx, feeOptionKey) as Promise<string>;
164
- }
165
-
166
- throw new SwapKitError('core_swap_quote_mode_not_supported', { quoteMode });
167
- } catch (error) {
168
- throw new SwapKitError('core_swap_transaction_error', error);
169
- }
170
- };
171
-
172
- getWalletByChain = async (chain: Chain, potentialScamFilter?: boolean) => {
173
- const address = this.getAddress(chain);
174
- if (!address) return null;
175
-
176
- const balance = (await this.getWallet(chain)?.getBalance(address, potentialScamFilter)) ?? [
177
- AssetValue.fromChainOrSignature(chain),
178
- ];
179
-
180
- this.connectedChains[chain] = {
181
- address,
182
- balance,
183
- walletType: this.connectedChains[chain]?.walletType as WalletOption,
184
- };
185
-
186
- return { ...this.connectedChains[chain] };
187
- };
188
-
189
- approveAssetValue = (assetValue: AssetValue, contractAddress?: string) =>
190
- this.#approve({ assetValue, type: 'approve', contractAddress });
8
+ ChainWallet,
9
+ ConnectWalletParamsLocal as ConnectWalletParams,
10
+ SwapWithRouteParams,
11
+ } from "../types.ts";
191
12
 
192
- isAssetValueApproved = (assetValue: AssetValue, contractAddress?: string) =>
193
- this.#approve<boolean>({ assetValue, contractAddress, type: 'checkOnly' });
13
+ export type PluginName = "thorchain" | "chainflip" | "mayachain";
194
14
 
195
- validateAddress = ({ address, chain }: { address: string; chain: Chain }) =>
196
- this.getWallet(chain)?.validateAddress?.(address);
15
+ export enum ApproveMode {
16
+ Approve = "approve",
17
+ CheckOnly = "checkOnly",
18
+ }
197
19
 
198
- transfer = async (params: CoreTxParams & { router?: string }) => {
199
- const walletInstance = this.connectedWallets[params.assetValue.chain];
200
- if (!walletInstance) throw new SwapKitError('core_wallet_connection_not_found');
20
+ export type ApproveReturnType<T extends ApproveMode> = T extends "checkOnly"
21
+ ? Promise<boolean>
22
+ : Promise<string>;
23
+
24
+ type SwapKitPlugins = {
25
+ [K in PluginName]?: ProviderMethods;
26
+ };
27
+
28
+ type GenericSwapParams = {
29
+ buyAsset: AssetValue;
30
+ sellAsset: AssetValue;
31
+ recipient: string;
32
+ };
33
+
34
+ export type SwapParams = (SwapWithRouteParams | GenericSwapParams) & {
35
+ provider?: {
36
+ name: PluginName;
37
+ config: Record<string, Todo>;
38
+ };
39
+ };
40
+
41
+ export type SwapKitReturnType = SwapKitPlugins & {
42
+ getAddress: (chain: Chain) => string;
43
+ getWallet: (chain: Chain) => ChainWallet<Chain> | undefined;
44
+ getWalletWithBalance: (
45
+ chain: Chain,
46
+ potentialScamFilter?: boolean,
47
+ ) => Promise<ChainWallet<Chain>>;
48
+ getBalance: (chain: Chain, potentialScamFilter?: boolean) => AssetValue[];
49
+ getExplorerTxUrl: typeof getTxUrl;
50
+ getExplorerAddressUrl: typeof getAddressUrl;
51
+ swap: (params: SwapParams) => Promise<string>;
52
+ validateAddress: (params: { address: string; chain: Chain }) =>
53
+ | boolean
54
+ | Promise<boolean>
55
+ | undefined;
56
+ approveAssetValue: (assetValue: AssetValue, contractAddress: string) => boolean | Promise<string>;
57
+ isAssetValueApproved: (
58
+ assetValue: AssetValue,
59
+ contractAddress: string,
60
+ ) => boolean | Promise<boolean>;
61
+ };
62
+
63
+ export type Wallets = { [K in Chain]?: ChainWallet<K> };
64
+ export type AvailableProviders<T> = T | { [K in PluginName]?: ProviderMethods };
65
+ export type ProviderMethods = {
66
+ swap: (swapParams: SwapParams) => Promise<string>;
67
+ [key: string]: Todo;
68
+ };
69
+
70
+ export type SwapKitPlugin = ({ wallets, stagenet }: { wallets: Wallets; stagenet?: boolean }) => {
71
+ name: PluginName;
72
+ methods: ProviderMethods;
73
+ };
74
+
75
+ export type SwapKitWallet = {
76
+ connectMethodName: string;
77
+ connect: (params: ConnectWalletParams) => (connectParams: Todo) => undefined | string;
78
+ };
79
+
80
+ export function SwapKit<
81
+ ExtendedProviders extends {},
82
+ ConnectWalletMethods = Record<string, ReturnType<SwapKitWallet["connect"]>>,
83
+ >({
84
+ stagenet,
85
+ wallets,
86
+ plugins,
87
+ config = {},
88
+ apis = {},
89
+ rpcUrls = {},
90
+ }: {
91
+ plugins: SwapKitPlugin[];
92
+ stagenet: boolean;
93
+ wallets: SwapKitWallet[];
94
+ config?: Record<string, Todo>;
95
+ apis: Record<string, Todo>;
96
+ rpcUrls: Record<string, Todo>;
97
+ }): SwapKitReturnType & ConnectWalletMethods & AvailableProviders<ExtendedProviders> {
98
+ const connectedWallets: Wallets = {};
99
+ const availablePlugins: AvailableProviders<ExtendedProviders> = {};
100
+
101
+ for (const plugin of plugins) {
102
+ const { name, methods } = plugin({ wallets: connectedWallets, stagenet });
103
+
104
+ availablePlugins[name] = methods;
105
+ }
201
106
 
202
- try {
203
- return await walletInstance.transfer(this.#prepareTxParams(params));
204
- } catch (error) {
205
- throw new SwapKitError('core_swap_transaction_error', error);
206
- }
207
- };
107
+ const connectWalletMethods = wallets.reduce((acc, wallet) => {
108
+ // @ts-expect-error
109
+ acc[wallet.connectMethodName] = wallet.connect({
110
+ addChain,
111
+ config,
112
+ apis,
113
+ rpcUrls,
114
+ });
208
115
 
209
- deposit = async ({
210
- assetValue,
211
- recipient,
212
- router,
213
- ...rest
214
- }: CoreTxParams & { router?: string }) => {
215
- const { chain, symbol, ticker } = assetValue;
216
- const walletInstance = this.connectedWallets[chain];
217
- if (!walletInstance) throw new SwapKitError('core_wallet_connection_not_found');
218
-
219
- const params = this.#prepareTxParams({ assetValue, recipient, router, ...rest });
220
-
221
- try {
222
- switch (chain) {
223
- case Chain.THORChain: {
224
- const wallet = walletInstance as ThorchainWallet;
225
- return await (recipient === '' ? wallet.deposit(params) : wallet.transfer(params));
226
- }
227
-
228
- case Chain.Ethereum:
229
- case Chain.BinanceSmartChain:
230
- case Chain.Avalanche: {
231
- const { getChecksumAddressFromAsset } = await import('@swapkit/toolbox-evm');
232
-
233
- const abi =
234
- chain === Chain.Avalanche
235
- ? TCAvalancheDepositABI
236
- : chain === Chain.BinanceSmartChain
237
- ? TCBscDepositABI
238
- : TCEthereumVaultAbi;
239
-
240
- return (await (
241
- walletInstance as EVMWallet<typeof AVAXToolbox | typeof ETHToolbox | typeof BSCToolbox>
242
- ).call({
243
- abi,
244
- contractAddress:
245
- router || ((await this.#getInboundDataByChain(chain as EVMChain)).router as string),
246
- funcName: 'depositWithExpiry',
247
- funcParams: [
248
- recipient,
249
- getChecksumAddressFromAsset({ chain, symbol, ticker }, chain),
250
- // TODO: (@Towan) Re-Check on that conversion 🙏
251
- assetValue.baseValueBigInt.toString(),
252
- params.memo,
253
- rest.expiration,
254
- ],
255
- txOverrides: { from: params.from, value: assetValue.baseValueBigInt },
256
- })) as Promise<string>;
257
- }
258
-
259
- default: {
260
- return await walletInstance.transfer(params);
261
- }
262
- }
263
- } catch (error: any) {
264
- const errorMessage = (error?.message || error?.toString()).toLowerCase();
265
- const isInsufficientFunds = errorMessage?.includes('insufficient funds');
266
- const isGas = errorMessage?.includes('gas');
267
- const isServer = errorMessage?.includes('server');
268
- const errorKey: Keys = isInsufficientFunds
269
- ? 'core_transaction_deposit_insufficient_funds_error'
270
- : isGas
271
- ? 'core_transaction_deposit_gas_error'
272
- : isServer
273
- ? 'core_transaction_deposit_server_error'
274
- : 'core_transaction_deposit_error';
275
-
276
- throw new SwapKitError(errorKey, error);
277
- }
278
- };
116
+ return acc;
117
+ }, {} as ConnectWalletMethods);
279
118
 
280
119
  /**
281
- * TC related Methods
120
+ * @Private
121
+ * Internal helpers
282
122
  */
283
- createLiquidity = async ({
284
- runeAssetValue,
285
- assetValue,
286
- }: {
287
- runeAssetValue: AssetValue;
288
- assetValue: AssetValue;
289
- }) => {
290
- if (runeAssetValue.lte(0) || assetValue.lte(0)) {
291
- throw new SwapKitError('core_transaction_create_liquidity_invalid_params');
292
- }
123
+ function getSwapKitPlugin(pluginName?: PluginName) {
124
+ const plugin =
125
+ (availablePlugins as SwapKitPlugins)[pluginName as PluginName] ||
126
+ Object.values(availablePlugins)[0];
293
127
 
294
- let runeTx = '';
295
- let assetTx = '';
296
-
297
- try {
298
- runeTx = await this.#depositToPool({
299
- assetValue: runeAssetValue,
300
- memo: getMemoFor(MemoType.DEPOSIT, {
301
- ...assetValue,
302
- address: this.getAddress(assetValue.chain),
303
- }),
304
- });
305
- } catch (error) {
306
- throw new SwapKitError('core_transaction_create_liquidity_rune_error', error);
128
+ if (!plugin) {
129
+ throw new SwapKitError("core_plugin_not_found", "Could not find the requested plugin");
307
130
  }
308
131
 
309
- try {
310
- assetTx = await this.#depositToPool({
311
- assetValue,
312
- memo: getMemoFor(MemoType.DEPOSIT, {
313
- ...assetValue,
314
- address: this.getAddress(Chain.THORChain),
315
- }),
316
- });
317
- } catch (error) {
318
- throw new SwapKitError('core_transaction_create_liquidity_asset_error', error);
319
- }
132
+ return plugin;
133
+ }
320
134
 
321
- return { runeTx, assetTx };
322
- };
135
+ function addChain(connectWallet: ChainWallet<Chain>) {
136
+ (connectedWallets[connectWallet.chain as Chain] as ChainWallet<Chain>) = connectWallet;
137
+ }
323
138
 
324
- addLiquidity = async ({
325
- poolIdentifier,
326
- runeAssetValue,
139
+ /**
140
+ * @Private
141
+ * Wallet interaction helpers
142
+ */
143
+ function approve<T extends ApproveMode>({
327
144
  assetValue,
328
- runeAddr,
329
- assetAddr,
330
- isPendingSymmAsset,
331
- mode = 'sym',
145
+ type = "checkOnly" as T,
146
+ contractAddress,
332
147
  }: {
333
- poolIdentifier: string;
334
- runeAssetValue: AssetValue;
148
+ type: T;
335
149
  assetValue: AssetValue;
336
- isPendingSymmAsset?: boolean;
337
- runeAddr?: string;
338
- assetAddr?: string;
339
- mode?: 'sym' | 'rune' | 'asset';
340
- }) => {
341
- const [chain, ...symbolPath] = poolIdentifier.split('.') as [Chain, string];
342
- const isSym = mode === 'sym';
343
- const runeTransfer = runeAssetValue?.gt(0) && (isSym || mode === 'rune');
344
- const assetTransfer = assetValue?.gt(0) && (isSym || mode === 'asset');
345
- const includeRuneAddress = isPendingSymmAsset || runeTransfer;
346
- const runeAddress = includeRuneAddress ? runeAddr || this.getAddress(Chain.THORChain) : '';
347
- const assetAddress = isSym || mode === 'asset' ? assetAddr || this.getAddress(chain) : '';
348
-
349
- if (!runeTransfer && !assetTransfer) {
350
- throw new SwapKitError('core_transaction_add_liquidity_invalid_params');
351
- }
352
- if (includeRuneAddress && !runeAddress) {
353
- throw new SwapKitError('core_transaction_add_liquidity_no_rune_address');
354
- }
150
+ contractAddress: string;
151
+ }): ApproveReturnType<T> {
152
+ const { address, chain, isGasAsset, isSynthetic } = assetValue;
153
+ const isEVMChain = [Chain.Ethereum, Chain.Avalanche, Chain.BinanceSmartChain].includes(chain);
154
+ const isNativeEVM = isEVMChain && isGasAsset;
355
155
 
356
- let runeTx, assetTx;
357
- const sharedParams = { chain, symbol: symbolPath.join('.') };
358
-
359
- if (runeTransfer && runeAssetValue) {
360
- try {
361
- runeTx = await this.#depositToPool({
362
- assetValue: runeAssetValue,
363
- memo: getMemoFor(MemoType.DEPOSIT, { ...sharedParams, address: assetAddress }),
364
- });
365
- } catch (error) {
366
- throw new SwapKitError('core_transaction_add_liquidity_rune_error', error);
367
- }
156
+ if (isNativeEVM || !isEVMChain || isSynthetic) {
157
+ return Promise.resolve(type === "checkOnly" ? true : "approved") as ApproveReturnType<T>;
368
158
  }
369
159
 
370
- if (assetTransfer && assetValue) {
371
- try {
372
- assetTx = await this.#depositToPool({
373
- assetValue,
374
- memo: getMemoFor(MemoType.DEPOSIT, { ...sharedParams, address: runeAddress }),
375
- });
376
- } catch (error) {
377
- throw new SwapKitError('core_transaction_add_liquidity_asset_error', error);
378
- }
379
- }
160
+ const walletMethods =
161
+ connectedWallets[chain as Chain.Ethereum | Chain.BinanceSmartChain | Chain.Avalanche];
380
162
 
381
- return { runeTx, assetTx };
382
- };
163
+ const walletAction = type === "checkOnly" ? walletMethods?.isApproved : walletMethods?.approve;
383
164
 
384
- withdraw = async ({
385
- memo,
386
- assetValue,
387
- percent,
388
- from,
389
- to,
390
- }: {
391
- memo?: string;
392
- assetValue: AssetValue;
393
- percent: number;
394
- from: 'sym' | 'rune' | 'asset';
395
- to: 'sym' | 'rune' | 'asset';
396
- }) => {
397
- const targetAsset =
398
- to === 'rune'
399
- ? AssetValue.fromChainOrSignature(Chain.THORChain)
400
- : (from === 'sym' && to === 'sym') || from === 'rune' || from === 'asset'
401
- ? undefined
402
- : assetValue;
403
-
404
- try {
405
- const txHash = await this.#depositToPool({
406
- assetValue: getMinAmountByChain(from === 'asset' ? assetValue.chain : Chain.THORChain),
407
- memo:
408
- memo ||
409
- getMemoFor(MemoType.WITHDRAW, {
410
- symbol: assetValue.symbol,
411
- chain: assetValue.chain,
412
- ticker: assetValue.ticker,
413
- basisPoints: Math.max(10000, Math.round(percent * 100)),
414
- targetAssetString: targetAsset?.toString(),
415
- singleSide: false,
416
- }),
417
- });
418
-
419
- return txHash;
420
- } catch (error) {
421
- throw new SwapKitError('core_transaction_withdraw_error', error);
422
- }
423
- };
424
-
425
- savings = async ({
426
- assetValue,
427
- memo,
428
- percent,
429
- type,
430
- }: { assetValue: AssetValue; memo?: string } & (
431
- | { type: 'add'; percent?: undefined }
432
- | { type: 'withdraw'; percent: number }
433
- )) => {
434
- const memoType = type === 'add' ? MemoType.DEPOSIT : MemoType.WITHDRAW;
435
- const memoString =
436
- memo ||
437
- getMemoFor(memoType, {
438
- ticker: assetValue.ticker,
439
- symbol: assetValue.symbol,
440
- chain: assetValue.chain,
441
- singleSide: true,
442
- basisPoints: percent ? Math.max(10000, Math.round(percent * 100)) : undefined,
443
- });
444
-
445
- return this.#depositToPool({ assetValue, memo: memoString });
446
- };
447
-
448
- loan = ({
449
- assetValue,
450
- memo,
451
- minAmount,
452
- type,
453
- }: {
454
- assetValue: AssetValue;
455
- memo?: string;
456
- minAmount: AssetValue;
457
- type: 'open' | 'close';
458
- }) =>
459
- this.#depositToPool({
460
- assetValue,
461
- memo:
462
- memo ||
463
- getMemoFor(type === 'open' ? MemoType.OPEN_LOAN : MemoType.CLOSE_LOAN, {
464
- asset: assetValue.toString(),
465
- minAmount: minAmount.toString(),
466
- address: this.getAddress(assetValue.chain),
467
- }),
468
- });
165
+ if (!walletAction) throw new SwapKitError("core_wallet_connection_not_found");
469
166
 
470
- nodeAction = ({
471
- type,
472
- assetValue,
473
- address,
474
- }: { address: string } & (
475
- | { type: 'bond' | 'unbond'; assetValue: AssetValue }
476
- | { type: 'leave'; assetValue?: undefined }
477
- )) => {
478
- const memoType =
479
- type === 'bond' ? MemoType.BOND : type === 'unbond' ? MemoType.UNBOND : MemoType.LEAVE;
480
- const memo = getMemoFor(memoType, {
481
- address,
482
- unbondAmount: type === 'unbond' ? assetValue.baseValueNumber : undefined,
483
- });
167
+ const from = getAddress(chain);
484
168
 
485
- return this.#thorchainTransfer({
486
- memo,
487
- assetValue: type === 'bond' ? assetValue : getMinAmountByChain(Chain.THORChain),
488
- });
489
- };
169
+ if (!(address && from)) throw new SwapKitError("core_approve_asset_address_or_from_not_found");
490
170
 
491
- registerThorname = ({
492
- assetValue,
493
- ...param
494
- }: ThornameRegisterParam & { assetValue: AssetValue }) =>
495
- this.#thorchainTransfer({ assetValue, memo: getMemoFor(MemoType.THORNAME_REGISTER, param) });
496
-
497
- extend = ({ wallets, config, apis = {}, rpcUrls = {} }: ExtendParams<T>) => {
498
- try {
499
- wallets.forEach((wallet) => {
500
- // @ts-expect-error ANCHOR - Not Worth
501
- this[wallet.connectMethodName] = wallet.connect({
502
- addChain: this.#addConnectedChain,
503
- config: config || {},
504
- apis,
505
- rpcUrls,
506
- });
507
- });
508
- } catch (error) {
509
- throw new SwapKitError('core_extend_error', error);
510
- }
511
- };
171
+ const spenderAddress = contractAddress;
512
172
 
513
- estimateMaxSendableAmount = async ({
514
- chain,
515
- params,
516
- }: {
517
- chain: Chain;
518
- params: { from: string; recipient: string; assetValue: AssetValue };
519
- }) => {
520
- const walletMethods = this.getWallet<typeof chain>(chain);
521
-
522
- switch (chain) {
523
- case Chain.Arbitrum:
524
- case Chain.Avalanche:
525
- case Chain.BinanceSmartChain:
526
- case Chain.Ethereum:
527
- case Chain.Optimism:
528
- case Chain.Polygon: {
529
- const { estimateMaxSendableAmount } = await import('@swapkit/toolbox-evm');
530
- return estimateMaxSendableAmount({
531
- ...params,
532
- toolbox: walletMethods as EVMToolbox,
533
- });
534
- }
535
-
536
- case Chain.Bitcoin:
537
- case Chain.BitcoinCash:
538
- case Chain.Dogecoin:
539
- case Chain.Litecoin:
540
- return (walletMethods as UTXOToolbox).estimateMaxSendableAmount(params);
541
-
542
- case Chain.Binance:
543
- case Chain.THORChain:
544
- case Chain.Cosmos: {
545
- const { estimateMaxSendableAmount } = await import('@swapkit/toolbox-cosmos');
546
- return estimateMaxSendableAmount({
547
- ...params,
548
- toolbox: walletMethods as CosmosLikeToolbox,
549
- });
550
- }
551
-
552
- default:
553
- throw new SwapKitError('core_estimated_max_spendable_chain_not_supported');
554
- }
555
- };
173
+ return walletAction({
174
+ amount: assetValue.getBaseValue("bigint"),
175
+ assetAddress: address,
176
+ from,
177
+ spenderAddress,
178
+ }) as ApproveReturnType<T>;
179
+ }
556
180
 
557
181
  /**
558
- * Wallet connection methods
182
+ * @Public
183
+ * Wallet helpers
559
184
  */
560
- connectXDEFI = async (_chains: Chain[]): Promise<void> => {
561
- throw new SwapKitError('core_wallet_xdefi_not_installed');
562
- };
563
- connectEVMWallet = async (_chains: Chain[] | Chain, _wallet: EVMWalletOptions): Promise<void> => {
564
- throw new SwapKitError('core_wallet_evmwallet_not_installed');
565
- };
566
- connectWalletconnect = async (_chains: Chain[], _options?: any): Promise<void> => {
567
- throw new SwapKitError('core_wallet_walletconnect_not_installed');
568
- };
569
- connectKeystore = async (_chains: Chain[], _phrase: string): Promise<void> => {
570
- throw new SwapKitError('core_wallet_keystore_not_installed');
571
- };
572
- connectLedger = async (_chains: Chain, _derivationPath: number[]): Promise<void> => {
573
- throw new SwapKitError('core_wallet_ledger_not_installed');
574
- };
575
- connectTrezor = async (_chains: Chain, _derivationPath: number[]): Promise<void> => {
576
- throw new SwapKitError('core_wallet_trezor_not_installed');
577
- };
578
- connectKeplr = async (_chain: Chain): Promise<void> => {
579
- throw new SwapKitError('core_wallet_keplr_not_installed');
580
- };
581
- connectOkx = async (_chains: Chain[]): Promise<void> => {
582
- throw new SwapKitError('core_wallet_okx_not_installed');
583
- };
584
- disconnectChain = (chain: Chain) => {
585
- this.connectedChains[chain] = null;
586
- this.connectedWallets[chain] = null;
587
- };
588
-
589
- #getInboundDataByChain = async (chain: Chain) => {
590
- if (chain === Chain.THORChain) {
591
- return {
592
- gas_rate: '0',
593
- router: '0',
594
- address: '',
595
- halted: false,
596
- chain: Chain.THORChain,
597
- };
598
- }
599
- const inboundData = await getInboundData(this.stagenet);
600
- const chainAddressData = inboundData.find((item) => item.chain === chain);
601
-
602
- if (!chainAddressData) throw new SwapKitError('core_inbound_data_not_found');
603
- if (chainAddressData?.halted) throw new SwapKitError('core_chain_halted');
604
-
605
- return chainAddressData;
606
- };
607
-
608
- #addConnectedChain = ({ chain, wallet, walletMethods }: AddChainWalletParams) => {
609
- this.connectedChains[chain] = wallet;
610
- this.connectedWallets[chain] = walletMethods;
611
- };
612
-
613
- #approve = async <T = string>({
614
- assetValue: { baseValueBigInt, address, chain, isGasAsset, isSynthetic },
615
- type = 'checkOnly',
616
- contractAddress,
617
- }: {
618
- assetValue: AssetValue;
619
- type?: 'checkOnly' | 'approve';
620
- contractAddress?: string;
621
- }) => {
622
- const isEVMChain = [Chain.Ethereum, Chain.Avalanche, Chain.BinanceSmartChain].includes(chain);
623
- const isNativeEVM = isEVMChain && isGasAsset;
185
+ function getWallet(chain: Chain) {
186
+ return connectedWallets[chain];
187
+ }
188
+ function getAddress(chain: Chain) {
189
+ return getWallet(chain)?.address || "";
190
+ }
191
+ function getBalance(chain: Chain) {
192
+ return getWallet(chain)?.balance || [];
193
+ }
194
+ /**
195
+ * TODO: Figure out validation without connecting to wallet
196
+ */
197
+ function validateAddress({ address, chain }: { address: string; chain: Chain }) {
198
+ return getWallet(chain)?.validateAddress?.(address);
199
+ }
624
200
 
625
- if (isNativeEVM || !isEVMChain || isSynthetic) return true;
201
+ async function getWalletWithBalance(chain: Chain, potentialScamFilter?: boolean) {
202
+ const defaultBalance = [AssetValue.fromChainOrSignature(chain)];
203
+ const wallet = getWallet(chain);
626
204
 
627
- const walletMethods = this.connectedWallets[chain as EVMChain];
628
- const walletAction = type === 'checkOnly' ? walletMethods?.isApproved : walletMethods?.approve;
205
+ if (!wallet) {
206
+ throw new SwapKitError("core_wallet_connection_not_found");
207
+ }
629
208
 
630
- if (!walletAction) throw new SwapKitError('core_wallet_connection_not_found');
209
+ const balance = await wallet?.getBalance(wallet.address, potentialScamFilter);
210
+ wallet.balance = balance?.length ? balance : defaultBalance;
631
211
 
632
- const from = this.getAddress(chain);
212
+ return wallet;
213
+ }
633
214
 
634
- if (!address || !from) throw new SwapKitError('core_approve_asset_address_or_from_not_found');
215
+ /**
216
+ * @Public
217
+ * Wallet interaction methods
218
+ */
219
+ function approveAssetValue(assetValue: AssetValue, contractAddress: string) {
220
+ return approve({ assetValue, contractAddress, type: ApproveMode.Approve });
221
+ }
635
222
 
636
- const spenderAddress =
637
- contractAddress || ((await this.#getInboundDataByChain(chain)).router as string);
223
+ function isAssetValueApproved(assetValue: AssetValue, contractAddress: string) {
224
+ return approve({ assetValue, contractAddress, type: ApproveMode.CheckOnly });
225
+ }
638
226
 
639
- return walletAction({
640
- amount: baseValueBigInt,
641
- assetAddress: address,
642
- from,
643
- spenderAddress,
644
- }) as Promise<T>;
645
- };
227
+ function swap({ provider, ...rest }: SwapParams) {
228
+ const plugin = getSwapKitPlugin(provider?.name);
646
229
 
647
- #depositToPool = async ({
648
- assetValue,
649
- memo,
650
- feeOptionKey = FeeOption.Fast,
651
- }: {
652
- assetValue: AssetValue;
653
- memo: string;
654
- feeOptionKey?: FeeOption;
655
- }) => {
656
- const {
657
- gas_rate,
658
- router,
659
- address: poolAddress,
660
- } = await this.#getInboundDataByChain(assetValue.chain);
661
- const feeRate = (parseInt(gas_rate) || 0) * gasFeeMultiplier[feeOptionKey];
662
-
663
- return this.deposit({
664
- assetValue,
665
- recipient: poolAddress,
666
- memo,
667
- router,
668
- feeRate,
669
- });
670
- };
230
+ return plugin.swap({ provider, ...rest });
231
+ }
671
232
 
672
- #thorchainTransfer = async ({ memo, assetValue }: { assetValue: AssetValue; memo: string }) => {
673
- const mimir = await getMimirData(this.stagenet);
233
+ return {
234
+ ...availablePlugins,
235
+ ...connectWalletMethods,
674
236
 
675
- // check if trading is halted or not
676
- if (mimir['HALTCHAINGLOBAL'] >= 1 || mimir['HALTTHORCHAIN'] >= 1) {
677
- throw new SwapKitError('core_chain_halted');
678
- }
237
+ getExplorerAddressUrl: getAddressUrl,
238
+ getExplorerTxUrl: getTxUrl,
679
239
 
680
- return this.deposit({ assetValue, recipient: '', memo });
240
+ approveAssetValue,
241
+ getAddress,
242
+ getBalance,
243
+ getWallet,
244
+ getWalletWithBalance,
245
+ isAssetValueApproved,
246
+ swap,
247
+ validateAddress,
681
248
  };
682
-
683
- #prepareTxParams = ({ assetValue, ...restTxParams }: CoreTxParams & { router?: string }) => ({
684
- ...restTxParams,
685
- memo: restTxParams.memo || '',
686
- from: this.getAddress(assetValue.chain),
687
- assetValue,
688
- });
689
249
  }