@swapkit/core 1.0.0-rc.11 → 1.0.0-rc.110

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 (43) hide show
  1. package/dist/index.cjs +2 -2
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.ts +160 -26
  4. package/dist/index.es.js +6480 -1095
  5. package/dist/index.es.js.map +1 -0
  6. package/package.json +23 -30
  7. package/src/__tests__/helpers.test.ts +77 -0
  8. package/src/aggregator/contracts/avaxGeneric.ts +50 -50
  9. package/src/aggregator/contracts/avaxWoofi.ts +80 -80
  10. package/src/aggregator/contracts/bscGeneric.ts +59 -59
  11. package/src/aggregator/contracts/ethGeneric.ts +50 -50
  12. package/src/aggregator/contracts/index.ts +30 -28
  13. package/src/aggregator/contracts/pancakeV2.ts +80 -80
  14. package/src/aggregator/contracts/pangolin.ts +65 -65
  15. package/src/aggregator/contracts/routers/index.ts +58 -0
  16. package/src/aggregator/contracts/routers/kyber.ts +402 -0
  17. package/src/aggregator/contracts/routers/oneinch.ts +2188 -0
  18. package/src/aggregator/contracts/routers/pancakeswap.ts +340 -0
  19. package/src/aggregator/contracts/routers/pangolin.ts +340 -0
  20. package/src/aggregator/contracts/routers/sushiswap.ts +340 -0
  21. package/src/aggregator/contracts/routers/traderJoe.ts +340 -0
  22. package/src/aggregator/contracts/routers/uniswapv2.ts +340 -0
  23. package/src/aggregator/contracts/routers/uniswapv3.ts +254 -0
  24. package/src/aggregator/contracts/routers/woofi.ts +171 -0
  25. package/src/aggregator/contracts/sushiswap.ts +65 -65
  26. package/src/aggregator/contracts/traderJoe.ts +65 -65
  27. package/src/aggregator/contracts/uniswapV2.ts +65 -65
  28. package/src/aggregator/contracts/uniswapV2Leg.ts +70 -70
  29. package/src/aggregator/contracts/uniswapV3_100.ts +70 -70
  30. package/src/aggregator/contracts/uniswapV3_10000.ts +70 -70
  31. package/src/aggregator/contracts/uniswapV3_3000.ts +70 -70
  32. package/src/aggregator/contracts/uniswapV3_500.ts +70 -70
  33. package/src/aggregator/getSwapParams.ts +12 -12
  34. package/src/client/index.ts +212 -645
  35. package/src/client/old.ts +837 -0
  36. package/src/{client → helpers}/explorerUrls.ts +11 -10
  37. package/src/{client → helpers}/thornode.ts +5 -5
  38. package/src/index.ts +6 -4
  39. package/src/types.ts +149 -0
  40. package/dist/index-9e36735e.cjs +0 -1
  41. package/dist/index-cf1865cd.js +0 -649
  42. package/src/client/__tests__/helpers.test.ts +0 -69
  43. package/src/client/types.ts +0 -103
@@ -1,689 +1,256 @@
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
- };
8
+ ChainWallet,
9
+ ConnectWalletParamsLocal as ConnectWalletParams,
10
+ SwapWithRouteParams,
11
+ } from "../types.ts";
12
+
13
+ export type ProviderName = "thorchain" | "chainflip" | "mayachain";
14
+ export enum ApproveMode {
15
+ Approve = "approve",
16
+ CheckOnly = "checkOnly",
17
+ }
171
18
 
172
- getWalletByChain = async (chain: Chain, potentialScamFilter?: boolean) => {
173
- const address = this.getAddress(chain);
174
- if (!address) return null;
19
+ export type ApproveReturnType<T extends ApproveMode> = T extends "checkOnly"
20
+ ? Promise<boolean>
21
+ : Promise<string>;
175
22
 
176
- const balance = (await this.getWallet(chain)?.getBalance(address, potentialScamFilter)) ?? [
177
- AssetValue.fromChainOrSignature(chain),
178
- ];
23
+ type SwapKitProviders = {
24
+ [K in ProviderName]?: ProviderMethods;
25
+ };
179
26
 
180
- this.connectedChains[chain] = {
181
- address,
182
- balance,
183
- walletType: this.connectedChains[chain]?.walletType as WalletOption,
184
- };
27
+ type GenericSwapParams = {
28
+ buyAsset: AssetValue;
29
+ sellAsset: AssetValue;
30
+ recipient: string;
31
+ };
185
32
 
186
- return { ...this.connectedChains[chain] };
33
+ export type SwapParams = (SwapWithRouteParams | GenericSwapParams) & {
34
+ provider?: {
35
+ name: ProviderName;
36
+ config: Record<string, any>;
187
37
  };
38
+ };
39
+
40
+ export type SwapKitReturnType = SwapKitProviders & {
41
+ getAddress: (chain: Chain) => string;
42
+ getWallet: (chain: Chain) => ChainWallet<Chain> | undefined;
43
+ getWalletWithBalance: (
44
+ chain: Chain,
45
+ potentialScamFilter?: boolean,
46
+ ) => Promise<ChainWallet<Chain>>;
47
+ getBalance: (chain: Chain, potentialScamFilter?: boolean) => AssetValue[];
48
+ getExplorerTxUrl: (chain: Chain, txHash: string) => string;
49
+ getExplorerAddressUrl: (chain: Chain, address: string) => string;
50
+ swap: (params: SwapParams) => Promise<string>;
51
+ validateAddress: (params: { address: string; chain: Chain }) =>
52
+ | boolean
53
+ | Promise<boolean>
54
+ | undefined;
55
+ approveAssetValue: (assetValue: AssetValue, contractAddress: string) => boolean | Promise<string>;
56
+ isAssetValueApproved: (
57
+ assetValue: AssetValue,
58
+ contractAddress: string,
59
+ ) => boolean | Promise<boolean>;
60
+ };
61
+
62
+ export type Wallets = { [K in Chain]?: ChainWallet<K> };
63
+ export type AvailableProviders<T> = T | { [K in ProviderName]?: ProviderMethods };
64
+ export type ProviderMethods = {
65
+ swap: (swapParams: SwapParams) => Promise<string>;
66
+ [key: string]: any;
67
+ };
68
+
69
+ export type SwapKitProvider = ({ wallets, stagenet }: { wallets: Wallets; stagenet?: boolean }) => {
70
+ name: ProviderName;
71
+ methods: ProviderMethods;
72
+ };
73
+
74
+ export type SwapKitWallet = {
75
+ connectMethodName: string;
76
+ connect: (params: ConnectWalletParams) => (connectParams: any) => void;
77
+ };
78
+
79
+ export function SwapKit<
80
+ ExtendedProviders extends {},
81
+ ConnectWalletMethods extends Record<string, ReturnType<SwapKitWallet["connect"]>>,
82
+ >({
83
+ stagenet,
84
+ wallets,
85
+ providers,
86
+ config = {},
87
+ apis,
88
+ rpcUrls,
89
+ }: {
90
+ providers: SwapKitProvider[];
91
+ stagenet: boolean;
92
+ wallets: SwapKitWallet[];
93
+ config?: Record<string, any>;
94
+ apis: Record<string, any>;
95
+ rpcUrls: Record<string, any>;
96
+ }): SwapKitReturnType & ConnectWalletMethods & AvailableProviders<ExtendedProviders> {
97
+ const connectedWallets: Wallets = {};
98
+ const availableProviders: AvailableProviders<ExtendedProviders> = {};
99
+
100
+ for (const provider of providers) {
101
+ const { name, methods } = provider({ wallets: connectedWallets, stagenet });
102
+
103
+ availableProviders[name] = methods;
104
+ }
188
105
 
189
- approveAssetValue = (assetValue: AssetValue, contractAddress?: string) =>
190
- this.#approve({ assetValue, type: 'approve', contractAddress });
191
-
192
- isAssetValueApproved = (assetValue: AssetValue, contractAddress?: string) =>
193
- this.#approve<boolean>({ assetValue, contractAddress, type: 'checkOnly' });
194
-
195
- validateAddress = ({ address, chain }: { address: string; chain: Chain }) =>
196
- this.getWallet(chain)?.validateAddress?.(address);
106
+ const connectWalletMethods = wallets.reduce((acc, wallet) => {
107
+ (acc[wallet.connectMethodName] as ReturnType<SwapKitWallet["connect"]>) = wallet.connect({
108
+ addChain,
109
+ config,
110
+ apis,
111
+ rpcUrls,
112
+ });
197
113
 
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');
114
+ return acc;
115
+ }, {} as ConnectWalletMethods);
201
116
 
202
- try {
203
- return await walletInstance.transfer(this.#prepareTxParams(params));
204
- } catch (error) {
205
- throw new SwapKitError('core_swap_transaction_error', error);
117
+ /**
118
+ * @Private
119
+ * Internal helpers
120
+ */
121
+ function getProvider(providerName?: ProviderName) {
122
+ const provider =
123
+ (availableProviders as SwapKitProviders)[providerName as ProviderName] ||
124
+ Object.values(availableProviders)[0];
125
+
126
+ if (!provider) {
127
+ throw new SwapKitError(
128
+ "core_swap_provider_not_found",
129
+ "Could not find the requested provider",
130
+ );
206
131
  }
207
- };
208
132
 
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 });
133
+ return provider;
134
+ }
220
135
 
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
- };
136
+ function addChain(connectWallet: ChainWallet<Chain>) {
137
+ (connectedWallets[connectWallet.chain as Chain] as ChainWallet<Chain>) = connectWallet;
138
+ }
279
139
 
280
140
  /**
281
- * TC related Methods
141
+ * @Private
142
+ * Wallet interaction helpers
282
143
  */
283
- createLiquidity = async ({
284
- runeAssetValue,
144
+ function approve<T extends ApproveMode>({
285
145
  assetValue,
146
+ type = "checkOnly" as T,
147
+ contractAddress,
286
148
  }: {
287
- runeAssetValue: AssetValue;
149
+ type: T;
288
150
  assetValue: AssetValue;
289
- }) => {
290
- if (runeAssetValue.lte(0) || assetValue.lte(0)) {
291
- throw new SwapKitError('core_transaction_create_liquidity_invalid_params');
292
- }
293
-
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);
307
- }
151
+ contractAddress: string;
152
+ }): ApproveReturnType<T> {
153
+ const { address, chain, isGasAsset, isSynthetic } = assetValue;
154
+ const isEVMChain = [Chain.Ethereum, Chain.Avalanche, Chain.BinanceSmartChain].includes(chain);
155
+ const isNativeEVM = isEVMChain && isGasAsset;
308
156
 
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);
157
+ if (isNativeEVM || !isEVMChain || isSynthetic) {
158
+ return Promise.resolve(type === "checkOnly" ? true : "approved") as ApproveReturnType<T>;
319
159
  }
320
160
 
321
- return { runeTx, assetTx };
322
- };
161
+ const walletMethods =
162
+ connectedWallets[chain as Chain.Ethereum | Chain.BinanceSmartChain | Chain.Avalanche];
323
163
 
324
- addLiquidity = async ({
325
- poolIdentifier,
326
- runeAssetValue,
327
- assetValue,
328
- runeAddr,
329
- assetAddr,
330
- isPendingSymmAsset,
331
- mode = 'sym',
332
- }: {
333
- poolIdentifier: string;
334
- runeAssetValue: AssetValue;
335
- 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
- }
355
-
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
- }
368
- }
164
+ const walletAction = type === "checkOnly" ? walletMethods?.isApproved : walletMethods?.approve;
369
165
 
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
- }
166
+ if (!walletAction) throw new SwapKitError("core_wallet_connection_not_found");
380
167
 
381
- return { runeTx, assetTx };
382
- };
168
+ const from = getAddress(chain);
383
169
 
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;
170
+ if (!(address && from)) throw new SwapKitError("core_approve_asset_address_or_from_not_found");
403
171
 
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
- };
172
+ const spenderAddress = contractAddress;
424
173
 
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
- };
174
+ return walletAction({
175
+ amount: assetValue.getBaseValue("bigint"),
176
+ assetAddress: address,
177
+ from,
178
+ spenderAddress,
179
+ }) as ApproveReturnType<T>;
180
+ }
447
181
 
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
- });
182
+ /**
183
+ * @Public
184
+ * Wallet helpers
185
+ */
186
+ function getWallet(chain: Chain) {
187
+ return connectedWallets[chain];
188
+ }
189
+ function getAddress(chain: Chain) {
190
+ return getWallet(chain)?.address || "";
191
+ }
192
+ function getBalance(chain: Chain) {
193
+ return getWallet(chain)?.balance || [];
194
+ }
195
+ function getExplorerTxUrl(chain: Chain, txHash: string) {
196
+ return getTxUrl({ chain, txHash });
197
+ }
198
+ function getExplorerAddressUrl(chain: Chain, address: string) {
199
+ return getAddressUrl({ chain, address });
200
+ }
201
+ /**
202
+ * TODO: Figure out validation without connecting to wallet
203
+ */
204
+ function validateAddress({ address, chain }: { address: string; chain: Chain }) {
205
+ return getWallet(chain)?.validateAddress?.(address);
206
+ }
469
207
 
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
- });
208
+ async function getWalletWithBalance(chain: Chain, potentialScamFilter?: boolean) {
209
+ const defaultBalance = [AssetValue.fromChainOrSignature(chain)];
210
+ const wallet = getWallet(chain);
484
211
 
485
- return this.#thorchainTransfer({
486
- memo,
487
- assetValue: type === 'bond' ? assetValue : getMinAmountByChain(Chain.THORChain),
488
- });
489
- };
212
+ try {
213
+ if (!wallet) throw new SwapKitError("core_wallet_connection_not_found");
214
+ const balance = await wallet?.getBalance(wallet.address, potentialScamFilter);
490
215
 
491
- registerThorname = ({
492
- assetValue,
493
- ...param
494
- }: ThornameRegisterParam & { assetValue: AssetValue }) =>
495
- this.#thorchainTransfer({ assetValue, memo: getMemoFor(MemoType.THORNAME_REGISTER, param) });
216
+ wallet.balance = balance?.length ? balance : defaultBalance;
496
217
 
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
- });
218
+ return wallet;
508
219
  } catch (error) {
509
- throw new SwapKitError('core_extend_error', error);
220
+ throw new SwapKitError("core_wallet_connection_not_found", error);
510
221
  }
511
- };
512
-
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
- };
222
+ }
556
223
 
557
224
  /**
558
- * Wallet connection methods
225
+ * @Public
226
+ * Wallet interaction methods
559
227
  */
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;
624
-
625
- if (isNativeEVM || !isEVMChain || isSynthetic) return true;
626
-
627
- const walletMethods = this.connectedWallets[chain as EVMChain];
628
- const walletAction = type === 'checkOnly' ? walletMethods?.isApproved : walletMethods?.approve;
629
-
630
- if (!walletAction) throw new SwapKitError('core_wallet_connection_not_found');
631
-
632
- const from = this.getAddress(chain);
633
-
634
- if (!address || !from) throw new SwapKitError('core_approve_asset_address_or_from_not_found');
635
-
636
- const spenderAddress =
637
- contractAddress || ((await this.#getInboundDataByChain(chain)).router as string);
638
-
639
- return walletAction({
640
- amount: baseValueBigInt,
641
- assetAddress: address,
642
- from,
643
- spenderAddress,
644
- }) as Promise<T>;
645
- };
228
+ function approveAssetValue(assetValue: AssetValue, contractAddress: string) {
229
+ return approve({ assetValue, contractAddress, type: ApproveMode.Approve });
230
+ }
646
231
 
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
- };
232
+ function isAssetValueApproved(assetValue: AssetValue, contractAddress: string) {
233
+ return approve({ assetValue, contractAddress, type: ApproveMode.CheckOnly });
234
+ }
671
235
 
672
- #thorchainTransfer = async ({ memo, assetValue }: { assetValue: AssetValue; memo: string }) => {
673
- const mimir = await getMimirData(this.stagenet);
236
+ function swap({ provider: providerConfig, ...rest }: SwapParams) {
237
+ const provider = getProvider(providerConfig?.name);
674
238
 
675
- // check if trading is halted or not
676
- if (mimir['HALTCHAINGLOBAL'] >= 1 || mimir['HALTTHORCHAIN'] >= 1) {
677
- throw new SwapKitError('core_chain_halted');
678
- }
239
+ return provider.swap({ provider: providerConfig, ...rest });
240
+ }
679
241
 
680
- return this.deposit({ assetValue, recipient: '', memo });
242
+ return {
243
+ ...availableProviders,
244
+ ...connectWalletMethods,
245
+ approveAssetValue,
246
+ getAddress,
247
+ getBalance,
248
+ getExplorerAddressUrl,
249
+ getExplorerTxUrl,
250
+ getWallet,
251
+ getWalletWithBalance,
252
+ isAssetValueApproved,
253
+ swap,
254
+ validateAddress,
681
255
  };
682
-
683
- #prepareTxParams = ({ assetValue, ...restTxParams }: CoreTxParams & { router?: string }) => ({
684
- ...restTxParams,
685
- memo: restTxParams.memo || '',
686
- from: this.getAddress(assetValue.chain),
687
- assetValue,
688
- });
689
256
  }