@shapeshiftoss/swapper 20.6.0 → 20.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/constants.js +8 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/swappers/AcrossSwapper/constant.js +0 -7
- package/dist/cjs/swappers/BobGatewaySwapper/utils/constants.d.ts +1 -1
- package/dist/cjs/swappers/BobGatewaySwapper/utils/constants.js +0 -1
- package/dist/cjs/swappers/ChainflipSwapper/constants.d.ts +1 -1
- package/dist/cjs/swappers/ChainflipSwapper/constants.js +11 -1
- package/dist/cjs/swappers/ChainflipSwapper/types.d.ts +2 -1
- package/dist/cjs/swappers/ChainflipSwapper/types.js +1 -0
- package/dist/cjs/swappers/ChainflipSwapper/utils/helpers.d.ts +2 -2
- package/dist/cjs/swappers/DebridgeSwapper/constant.js +0 -4
- package/dist/cjs/swappers/FyndSwapper/utils/fyndService.js +1 -1
- package/dist/cjs/swappers/KyberSwapper/utils/kyberService.js +1 -1
- package/dist/cjs/swappers/PanoraSwapper/PanoraSwapper.d.ts +2 -0
- package/dist/cjs/swappers/PanoraSwapper/PanoraSwapper.js +5 -0
- package/dist/cjs/swappers/PanoraSwapper/endpoints.d.ts +2 -0
- package/dist/cjs/swappers/PanoraSwapper/endpoints.js +13 -0
- package/dist/cjs/swappers/PanoraSwapper/index.d.ts +2 -0
- package/dist/cjs/swappers/PanoraSwapper/index.js +7 -0
- package/dist/cjs/swappers/PanoraSwapper/swapperApi/getPanoraTradeQuote.d.ts +4 -0
- package/dist/cjs/swappers/PanoraSwapper/swapperApi/getPanoraTradeQuote.js +42 -0
- package/dist/cjs/swappers/PanoraSwapper/swapperApi/getPanoraTradeRate.d.ts +4 -0
- package/dist/cjs/swappers/PanoraSwapper/swapperApi/getPanoraTradeRate.js +36 -0
- package/dist/cjs/swappers/PanoraSwapper/types.d.ts +35 -0
- package/dist/cjs/swappers/PanoraSwapper/types.js +2 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/constants.d.ts +2 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/constants.js +6 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/fetchFromPanora.d.ts +7 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/fetchFromPanora.js +15 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/getPanoraStepData.d.ts +19 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/getPanoraStepData.js +41 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/getPanoraTradeContext.d.ts +17 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/getPanoraTradeContext.js +63 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/helpers.d.ts +25 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/helpers.js +129 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/helpers.test.js +40 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/panoraService.d.ts +5 -0
- package/dist/cjs/swappers/PanoraSwapper/utils/panoraService.js +10 -0
- package/dist/cjs/swappers/RelaySwapper/constant.js +0 -14
- package/dist/cjs/swappers/RelaySwapper/utils/relayTokenToAssetId.js +0 -70
- package/dist/cjs/swappers/SymbiosisSwapper/utils/constants.d.ts +0 -1
- package/dist/cjs/swappers/SymbiosisSwapper/utils/constants.js +1 -10
- package/dist/cjs/swappers/SymbiosisSwapper/utils/symbiosisService.js +1 -3
- package/dist/cjs/swappers/ZrxSwapper/utils/zrxService.js +1 -1
- package/dist/cjs/types.d.ts +3 -1
- package/dist/cjs/types.js +1 -0
- package/dist/cjs/utils/aptos/checkAptosSwapStatus.d.ts +4 -0
- package/dist/cjs/utils/aptos/checkAptosSwapStatus.js +27 -0
- package/dist/cjs/utils/aptos/getAptosNetworkFeeCryptoBaseUnit.d.ts +11 -0
- package/dist/cjs/utils/aptos/getAptosNetworkFeeCryptoBaseUnit.js +12 -0
- package/dist/cjs/utils/aptos/index.d.ts +2 -0
- package/dist/cjs/utils/aptos/index.js +2 -0
- package/dist/cjs/utils/cowswap/constants.js +0 -1
- package/dist/cjs/utils/cowswap/index.js +0 -2
- package/dist/cjs/utils/helpers.js +1 -0
- package/dist/cjs/utils/helpers.test.js +1 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/constants.js +8 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/swappers/AcrossSwapper/constant.js +2 -9
- package/dist/esm/swappers/BobGatewaySwapper/utils/constants.d.ts +1 -1
- package/dist/esm/swappers/BobGatewaySwapper/utils/constants.js +1 -2
- package/dist/esm/swappers/ChainflipSwapper/constants.d.ts +1 -1
- package/dist/esm/swappers/ChainflipSwapper/constants.js +12 -2
- package/dist/esm/swappers/ChainflipSwapper/types.d.ts +2 -1
- package/dist/esm/swappers/ChainflipSwapper/types.js +1 -0
- package/dist/esm/swappers/ChainflipSwapper/utils/helpers.d.ts +2 -2
- package/dist/esm/swappers/DebridgeSwapper/constant.js +2 -6
- package/dist/esm/swappers/FyndSwapper/utils/fyndService.js +1 -1
- package/dist/esm/swappers/KyberSwapper/utils/kyberService.js +1 -1
- package/dist/esm/swappers/PanoraSwapper/PanoraSwapper.d.ts +2 -0
- package/dist/esm/swappers/PanoraSwapper/PanoraSwapper.js +2 -0
- package/dist/esm/swappers/PanoraSwapper/endpoints.d.ts +2 -0
- package/dist/esm/swappers/PanoraSwapper/endpoints.js +10 -0
- package/dist/esm/swappers/PanoraSwapper/index.d.ts +2 -0
- package/dist/esm/swappers/PanoraSwapper/index.js +2 -0
- package/dist/esm/swappers/PanoraSwapper/swapperApi/getPanoraTradeQuote.d.ts +4 -0
- package/dist/esm/swappers/PanoraSwapper/swapperApi/getPanoraTradeQuote.js +38 -0
- package/dist/esm/swappers/PanoraSwapper/swapperApi/getPanoraTradeRate.d.ts +4 -0
- package/dist/esm/swappers/PanoraSwapper/swapperApi/getPanoraTradeRate.js +32 -0
- package/dist/esm/swappers/PanoraSwapper/types.d.ts +35 -0
- package/dist/esm/swappers/PanoraSwapper/types.js +1 -0
- package/dist/esm/swappers/PanoraSwapper/utils/constants.d.ts +2 -0
- package/dist/esm/swappers/PanoraSwapper/utils/constants.js +3 -0
- package/dist/esm/swappers/PanoraSwapper/utils/fetchFromPanora.d.ts +7 -0
- package/dist/esm/swappers/PanoraSwapper/utils/fetchFromPanora.js +11 -0
- package/dist/esm/swappers/PanoraSwapper/utils/getPanoraStepData.d.ts +19 -0
- package/dist/esm/swappers/PanoraSwapper/utils/getPanoraStepData.js +37 -0
- package/dist/esm/swappers/PanoraSwapper/utils/getPanoraTradeContext.d.ts +17 -0
- package/dist/esm/swappers/PanoraSwapper/utils/getPanoraTradeContext.js +59 -0
- package/dist/esm/swappers/PanoraSwapper/utils/helpers.d.ts +25 -0
- package/dist/esm/swappers/PanoraSwapper/utils/helpers.js +117 -0
- package/dist/esm/swappers/PanoraSwapper/utils/helpers.test.d.ts +1 -0
- package/dist/esm/swappers/PanoraSwapper/utils/helpers.test.js +38 -0
- package/dist/esm/swappers/PanoraSwapper/utils/panoraService.d.ts +5 -0
- package/dist/esm/swappers/PanoraSwapper/utils/panoraService.js +4 -0
- package/dist/esm/swappers/RelaySwapper/constant.js +2 -16
- package/dist/esm/swappers/RelaySwapper/utils/relayTokenToAssetId.js +0 -70
- package/dist/esm/swappers/SymbiosisSwapper/utils/constants.d.ts +0 -1
- package/dist/esm/swappers/SymbiosisSwapper/utils/constants.js +1 -10
- package/dist/esm/swappers/SymbiosisSwapper/utils/symbiosisService.js +1 -3
- package/dist/esm/swappers/ZrxSwapper/utils/zrxService.js +1 -1
- package/dist/esm/types.d.ts +3 -1
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils/aptos/checkAptosSwapStatus.d.ts +4 -0
- package/dist/esm/utils/aptos/checkAptosSwapStatus.js +23 -0
- package/dist/esm/utils/aptos/getAptosNetworkFeeCryptoBaseUnit.d.ts +11 -0
- package/dist/esm/utils/aptos/getAptosNetworkFeeCryptoBaseUnit.js +8 -0
- package/dist/esm/utils/aptos/index.d.ts +2 -0
- package/dist/esm/utils/aptos/index.js +2 -0
- package/dist/esm/utils/cowswap/constants.js +0 -1
- package/dist/esm/utils/cowswap/index.js +0 -2
- package/dist/esm/utils/helpers.js +2 -1
- package/dist/esm/utils/helpers.test.js +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { aptosChainId, ASSET_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip';
|
|
2
|
+
import { aptos } from '@shapeshiftoss/chain-adapters';
|
|
3
|
+
import { bn, bnOrZero, toBaseUnit } from '@shapeshiftoss/utils';
|
|
4
|
+
import { Err, Ok } from '@sniptt/monads';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import { TradeQuoteError } from '../../../types.js';
|
|
7
|
+
import { getInputOutputRate, makeSwapErrorRight, makeTradeStepBuildFailedErr } from '../../../utils/index.js';
|
|
8
|
+
import { getTreasuryAddressFromChainId } from '../../../utils/helpers.js';
|
|
9
|
+
const invalidResponse = (message, details) => makeSwapErrorRight({ message, details, code: TradeQuoteError.InvalidResponse });
|
|
10
|
+
export const assertValidTrade = ({ sellAsset, buyAsset, }) => {
|
|
11
|
+
if (sellAsset.chainId !== buyAsset.chainId) {
|
|
12
|
+
return Err(makeSwapErrorRight({
|
|
13
|
+
message: 'Panora does not support cross-chain trades',
|
|
14
|
+
code: TradeQuoteError.CrossChainNotSupported,
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
if (sellAsset.chainId !== aptosChainId) {
|
|
18
|
+
return Err(makeSwapErrorRight({
|
|
19
|
+
message: 'Panora only supports Aptos',
|
|
20
|
+
code: TradeQuoteError.UnsupportedChain,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
return Ok(true);
|
|
24
|
+
};
|
|
25
|
+
export const assetIdToPanoraToken = (assetId) => {
|
|
26
|
+
const { assetNamespace, assetReference } = fromAssetId(assetId);
|
|
27
|
+
return assetNamespace === ASSET_NAMESPACE.slip44 ? aptos.APT_COIN_TYPE : assetReference;
|
|
28
|
+
};
|
|
29
|
+
export const getPanoraFeeParams = (affiliateBps) => {
|
|
30
|
+
if (bnOrZero(affiliateBps).lte(0))
|
|
31
|
+
return {};
|
|
32
|
+
return {
|
|
33
|
+
integratorFeePercentage: bn(affiliateBps).div(100).toNumber(),
|
|
34
|
+
integratorFeeAddress: getTreasuryAddressFromChainId(aptosChainId),
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export const getPanoraRequestError = (error) => {
|
|
38
|
+
const { cause } = error;
|
|
39
|
+
if (!axios.isAxiosError(cause))
|
|
40
|
+
return error;
|
|
41
|
+
const status = cause.response?.status;
|
|
42
|
+
if (status === 429) {
|
|
43
|
+
return makeSwapErrorRight({
|
|
44
|
+
message: 'Panora rate limit exceeded',
|
|
45
|
+
code: TradeQuoteError.RateLimitExceeded,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const body = cause.response?.data;
|
|
49
|
+
const message = typeof body === 'object' &&
|
|
50
|
+
body !== null &&
|
|
51
|
+
'message' in body &&
|
|
52
|
+
typeof body.message === 'string'
|
|
53
|
+
? body.message
|
|
54
|
+
: cause.message;
|
|
55
|
+
return makeSwapErrorRight({
|
|
56
|
+
message: `Panora request failed: ${message}`,
|
|
57
|
+
code: TradeQuoteError.QueryFailed,
|
|
58
|
+
cause,
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
export const validatePanoraSwap = ({ response, sellAsset, buyAsset, }) => {
|
|
62
|
+
const { fromToken, toToken, feeToken, quotes } = response;
|
|
63
|
+
const quote = quotes?.[0];
|
|
64
|
+
if (!quote) {
|
|
65
|
+
return Err(makeSwapErrorRight({
|
|
66
|
+
message: 'Panora returned no route',
|
|
67
|
+
code: TradeQuoteError.NoRouteFound,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
// Panora amounts are in whole token units, so a decimals mismatch would sell and quote the wrong base unit amounts
|
|
71
|
+
if (fromToken.decimals !== sellAsset.precision || toToken.decimals !== buyAsset.precision) {
|
|
72
|
+
return Err(makeSwapErrorRight({
|
|
73
|
+
message: 'Panora token decimals do not match the trade assets',
|
|
74
|
+
code: TradeQuoteError.UnsupportedTradePair,
|
|
75
|
+
details: { fromToken, toToken },
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
if (!aptos.addressesMatch(feeToken.address, toToken.address)) {
|
|
79
|
+
return Err(invalidResponse('Panora charged its fee in a token other than the buy asset'));
|
|
80
|
+
}
|
|
81
|
+
const maybePayload = (() => {
|
|
82
|
+
try {
|
|
83
|
+
return Ok(aptos.extractEntryFunctionPayload(quote.rawTransaction.bcs));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return Err(makeTradeStepBuildFailedErr('validatePanoraSwap', error));
|
|
87
|
+
}
|
|
88
|
+
})();
|
|
89
|
+
if (maybePayload.isErr())
|
|
90
|
+
return Err(maybePayload.unwrapErr());
|
|
91
|
+
return Ok({ quote, payload: maybePayload.unwrap() });
|
|
92
|
+
};
|
|
93
|
+
export const getPanoraTradeAmounts = ({ input, quote, }) => {
|
|
94
|
+
const { sellAsset, buyAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit } = input;
|
|
95
|
+
// toTokenAmount is net of the integrator fee, which Panora charges in the buy asset
|
|
96
|
+
const buyAmountAfterFeesCryptoBaseUnit = toBaseUnit(quote.toTokenAmount, buyAsset.precision);
|
|
97
|
+
const affiliateFeeCryptoBaseUnit = toBaseUnit(quote.feeTokenAmount, buyAsset.precision);
|
|
98
|
+
return {
|
|
99
|
+
rate: getInputOutputRate({
|
|
100
|
+
sellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit,
|
|
101
|
+
buyAmountCryptoBaseUnit: buyAmountAfterFeesCryptoBaseUnit,
|
|
102
|
+
sellAsset,
|
|
103
|
+
buyAsset,
|
|
104
|
+
}),
|
|
105
|
+
buyAmountBeforeFeesCryptoBaseUnit: bn(buyAmountAfterFeesCryptoBaseUnit)
|
|
106
|
+
.plus(affiliateFeeCryptoBaseUnit)
|
|
107
|
+
.toFixed(),
|
|
108
|
+
buyAmountAfterFeesCryptoBaseUnit,
|
|
109
|
+
affiliateFee: bn(affiliateFeeCryptoBaseUnit).gt(0)
|
|
110
|
+
? {
|
|
111
|
+
assetId: buyAsset.assetId,
|
|
112
|
+
amountCryptoBaseUnit: affiliateFeeCryptoBaseUnit,
|
|
113
|
+
asset: buyAsset,
|
|
114
|
+
}
|
|
115
|
+
: undefined,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { aptosAssetId, aptosChainId } from '@shapeshiftoss/caip';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { getPanoraTradeAmounts, validatePanoraSwap } from './helpers.js';
|
|
4
|
+
const APT = { assetId: aptosAssetId, chainId: aptosChainId, precision: 8, symbol: 'APT' };
|
|
5
|
+
const USDC = {
|
|
6
|
+
assetId: 'aptos:1/fungible:0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',
|
|
7
|
+
chainId: aptosChainId,
|
|
8
|
+
precision: 6,
|
|
9
|
+
symbol: 'USDC',
|
|
10
|
+
};
|
|
11
|
+
// 10 APT -> USDC with a 0.6% integrator fee, from a live /swap response
|
|
12
|
+
const quote = { toTokenAmount: '5.573706', feeTokenAmount: '0.033644' };
|
|
13
|
+
describe('validatePanoraSwap', () => {
|
|
14
|
+
it('rejects tokens whose decimals do not match the trade assets', () => {
|
|
15
|
+
const response = {
|
|
16
|
+
fromToken: { address: '0xa', decimals: 8 },
|
|
17
|
+
toToken: { address: USDC.assetId, decimals: 8 },
|
|
18
|
+
feeToken: { address: USDC.assetId, decimals: 8 },
|
|
19
|
+
quotes: [quote],
|
|
20
|
+
};
|
|
21
|
+
const result = validatePanoraSwap({ response, sellAsset: APT, buyAsset: USDC });
|
|
22
|
+
expect(result.unwrapErr().code).toBe('UnsupportedTradePair');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('getPanoraTradeAmounts', () => {
|
|
26
|
+
it('adds the integrator fee back for the before-fees amount and reports it exactly', () => {
|
|
27
|
+
const input = {
|
|
28
|
+
sellAsset: APT,
|
|
29
|
+
buyAsset: USDC,
|
|
30
|
+
sellAmountIncludingProtocolFeesCryptoBaseUnit: '1000000000',
|
|
31
|
+
};
|
|
32
|
+
const amounts = getPanoraTradeAmounts({ input, quote });
|
|
33
|
+
expect(amounts.buyAmountAfterFeesCryptoBaseUnit).toBe('5573706');
|
|
34
|
+
expect(amounts.buyAmountBeforeFeesCryptoBaseUnit).toBe('5607350');
|
|
35
|
+
expect(amounts.affiliateFee?.amountCryptoBaseUnit).toBe('33644');
|
|
36
|
+
expect(amounts.affiliateFee?.assetId).toBe(USDC.assetId);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const panoraService: {
|
|
2
|
+
get: <T = any>(url: string, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
3
|
+
post: <T_1 = any>(url: string, data: any, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T_1, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
4
|
+
delete: <T_2 = any>(url: string, data: any, config?: import("axios").AxiosRequestConfig<any> | undefined) => Promise<import("@sniptt/monads").Result<import("axios").AxiosResponse<T_2, any, {}>, import("../../../index.js").SwapErrorRight>>;
|
|
5
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { makeSwapperAxiosServiceMonadic } from '../../../utils/index.js';
|
|
3
|
+
import { PANORA_API_URL } from './constants.js';
|
|
4
|
+
export const panoraService = makeSwapperAxiosServiceMonadic(axios.create({ baseURL: PANORA_API_URL, timeout: 15_000 }));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { arbitrumChainId, avalancheChainId, baseChainId, bobChainId, bscChainId, btcChainId, celoChainId, cronosChainId, elysiumChainId, ethChainId, gnosisChainId, hyperEvmChainId, inkChainId, katanaChainId, mantleChainId, megaethChainId, monadChainId, optimismChainId, plasmaChainId, polygonChainId, robinhoodChainId, seiChainId, solanaChainId, sonicChainId, tronChainId, unichainChainId, } from '@shapeshiftoss/caip';
|
|
2
2
|
import invert from 'lodash/invert';
|
|
3
3
|
import { zeroAddress } from 'viem';
|
|
4
|
-
import {
|
|
4
|
+
import { arbitrum, avalanche, base, bob, bsc, celo, cronos, gnosis, hyperEvm, ink, katana, mainnet as ethereum, mantle, megaeth, monad, optimism, plasma, polygon, robinhood, sei, sonic, tron, unichain, } from 'viem/chains';
|
|
5
5
|
import { TradeQuoteError } from '../../types.js';
|
|
6
6
|
import { RelayErrorCode } from './utils/types.js';
|
|
7
7
|
// https://docs.relay.link/resources/supported-chains
|
|
@@ -24,26 +24,12 @@ export const chainIdToRelayChainId = {
|
|
|
24
24
|
[megaethChainId]: megaeth.id,
|
|
25
25
|
[plasmaChainId]: plasma.id,
|
|
26
26
|
[katanaChainId]: katana.id,
|
|
27
|
-
[etherealChainId]: 5064014,
|
|
28
27
|
[celoChainId]: celo.id,
|
|
29
|
-
[flowEvmChainId]: flowMainnet.id,
|
|
30
|
-
[plumeChainId]: plumeMainnet.id,
|
|
31
|
-
[storyChainId]: story.id,
|
|
32
|
-
[zkSyncEraChainId]: zksync.id,
|
|
33
|
-
[blastChainId]: blast.id,
|
|
34
|
-
[worldChainChainId]: worldchain.id,
|
|
35
|
-
[hemiChainId]: hemi.id,
|
|
36
|
-
[lineaChainId]: linea.id,
|
|
37
|
-
[berachainChainId]: berachain.id,
|
|
38
|
-
[scrollChainId]: scroll.id,
|
|
39
28
|
[cronosChainId]: cronos.id,
|
|
40
29
|
[sonicChainId]: sonic.id,
|
|
41
30
|
[unichainChainId]: unichain.id,
|
|
42
31
|
[bobChainId]: bob.id,
|
|
43
|
-
[modeChainId]: mode.id,
|
|
44
|
-
[soneiumChainId]: soneium.id,
|
|
45
32
|
[seiChainId]: sei.id,
|
|
46
|
-
[abstractChainId]: abstract.id,
|
|
47
33
|
[robinhoodChainId]: robinhood.id,
|
|
48
34
|
[elysiumChainId]: 99901,
|
|
49
35
|
};
|
|
@@ -124,71 +124,16 @@ export const relayTokenToAssetId = (relayToken) => {
|
|
|
124
124
|
assetReference: ASSET_REFERENCE.MegaEth,
|
|
125
125
|
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
126
126
|
};
|
|
127
|
-
case CHAIN_REFERENCE.BerachainMainnet:
|
|
128
|
-
return {
|
|
129
|
-
assetReference: ASSET_REFERENCE.Berachain,
|
|
130
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
131
|
-
};
|
|
132
127
|
case CHAIN_REFERENCE.KatanaMainnet:
|
|
133
128
|
return {
|
|
134
129
|
assetReference: ASSET_REFERENCE.Katana,
|
|
135
130
|
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
136
131
|
};
|
|
137
|
-
case CHAIN_REFERENCE.EtherealMainnet:
|
|
138
|
-
return {
|
|
139
|
-
assetReference: ASSET_REFERENCE.Ethereal,
|
|
140
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
141
|
-
};
|
|
142
132
|
case CHAIN_REFERENCE.CeloMainnet:
|
|
143
133
|
return {
|
|
144
134
|
assetReference: ASSET_REFERENCE.Celo,
|
|
145
135
|
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
146
136
|
};
|
|
147
|
-
case CHAIN_REFERENCE.FlowEvmMainnet:
|
|
148
|
-
return {
|
|
149
|
-
assetReference: ASSET_REFERENCE.FlowEvm,
|
|
150
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
151
|
-
};
|
|
152
|
-
case CHAIN_REFERENCE.PlumeMainnet:
|
|
153
|
-
return {
|
|
154
|
-
assetReference: ASSET_REFERENCE.Plume,
|
|
155
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
156
|
-
};
|
|
157
|
-
case CHAIN_REFERENCE.StoryMainnet:
|
|
158
|
-
return {
|
|
159
|
-
assetReference: ASSET_REFERENCE.Story,
|
|
160
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
161
|
-
};
|
|
162
|
-
case CHAIN_REFERENCE.ZkSyncEraMainnet:
|
|
163
|
-
return {
|
|
164
|
-
assetReference: ASSET_REFERENCE.ZkSyncEra,
|
|
165
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
166
|
-
};
|
|
167
|
-
case CHAIN_REFERENCE.BlastMainnet:
|
|
168
|
-
return {
|
|
169
|
-
assetReference: ASSET_REFERENCE.Blast,
|
|
170
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
171
|
-
};
|
|
172
|
-
case CHAIN_REFERENCE.WorldChainMainnet:
|
|
173
|
-
return {
|
|
174
|
-
assetReference: ASSET_REFERENCE.WorldChain,
|
|
175
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
176
|
-
};
|
|
177
|
-
case CHAIN_REFERENCE.HemiMainnet:
|
|
178
|
-
return {
|
|
179
|
-
assetReference: ASSET_REFERENCE.Hemi,
|
|
180
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
181
|
-
};
|
|
182
|
-
case CHAIN_REFERENCE.LineaMainnet:
|
|
183
|
-
return {
|
|
184
|
-
assetReference: ASSET_REFERENCE.Linea,
|
|
185
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
186
|
-
};
|
|
187
|
-
case CHAIN_REFERENCE.ScrollMainnet:
|
|
188
|
-
return {
|
|
189
|
-
assetReference: ASSET_REFERENCE.Scroll,
|
|
190
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
191
|
-
};
|
|
192
137
|
case CHAIN_REFERENCE.CronosMainnet:
|
|
193
138
|
return {
|
|
194
139
|
assetReference: ASSET_REFERENCE.Cronos,
|
|
@@ -209,26 +154,11 @@ export const relayTokenToAssetId = (relayToken) => {
|
|
|
209
154
|
assetReference: ASSET_REFERENCE.Bob,
|
|
210
155
|
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
211
156
|
};
|
|
212
|
-
case CHAIN_REFERENCE.ModeMainnet:
|
|
213
|
-
return {
|
|
214
|
-
assetReference: ASSET_REFERENCE.Mode,
|
|
215
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
216
|
-
};
|
|
217
|
-
case CHAIN_REFERENCE.SoneiumMainnet:
|
|
218
|
-
return {
|
|
219
|
-
assetReference: ASSET_REFERENCE.Soneium,
|
|
220
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
221
|
-
};
|
|
222
157
|
case CHAIN_REFERENCE.SeiMainnet:
|
|
223
158
|
return {
|
|
224
159
|
assetReference: ASSET_REFERENCE.Sei,
|
|
225
160
|
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
226
161
|
};
|
|
227
|
-
case CHAIN_REFERENCE.AbstractMainnet:
|
|
228
|
-
return {
|
|
229
|
-
assetReference: ASSET_REFERENCE.Abstract,
|
|
230
|
-
assetNamespace: ASSET_NAMESPACE.slip44,
|
|
231
|
-
};
|
|
232
162
|
case CHAIN_REFERENCE.RobinhoodMainnet:
|
|
233
163
|
return {
|
|
234
164
|
assetReference: ASSET_REFERENCE.Robinhood,
|
|
@@ -11,7 +11,6 @@ export declare const SYMBIOSIS_MAX_SLIPPAGE_BPS = 1000;
|
|
|
11
11
|
export declare const SYMBIOSIS_PARTNER_ADDRESS = "0xF5AA59151bE6515C4Ca68A0282CF68B3eA4846fC";
|
|
12
12
|
export declare const SYMBIOSIS_PARTNER_FEE_BPS = "60";
|
|
13
13
|
export declare const SYMBIOSIS_PARTNER_FEE_DESCRIPTION = "Partner fee";
|
|
14
|
-
export declare const SYMBIOSIS_PARTNER_ID = "shapeshift";
|
|
15
14
|
export declare const DEFAULT_SYMBIOSIS_EVM_USER_ADDRESS = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
|
16
15
|
export declare const DEFAULT_SYMBIOSIS_TRON_USER_ADDRESS = "TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7";
|
|
17
16
|
export declare const SYMBIOSIS_TRON_BRIDGE_ENERGY = "470000";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { arbitrumChainId, avalancheChainId, baseChainId, bscChainId, cronosChainId, ethChainId, fromChainId, gnosisChainId, hyperEvmChainId, katanaChainId, mantleChainId, monadChainId, optimismChainId, plasmaChainId, polygonChainId, robinhoodChainId, seiChainId, sonicChainId, tronChainId, unichainChainId, } from '@shapeshiftoss/caip';
|
|
2
2
|
const SYMBIOSIS_TRON_CHAIN_ID = 728126428;
|
|
3
3
|
const SYMBIOSIS_SUPPORTED_EVM_CHAIN_IDS = [
|
|
4
4
|
ethChainId,
|
|
@@ -9,18 +9,11 @@ const SYMBIOSIS_SUPPORTED_EVM_CHAIN_IDS = [
|
|
|
9
9
|
optimismChainId,
|
|
10
10
|
baseChainId,
|
|
11
11
|
gnosisChainId,
|
|
12
|
-
zkSyncEraChainId,
|
|
13
|
-
lineaChainId,
|
|
14
|
-
scrollChainId,
|
|
15
12
|
mantleChainId,
|
|
16
|
-
modeChainId,
|
|
17
13
|
seiChainId,
|
|
18
14
|
cronosChainId,
|
|
19
15
|
sonicChainId,
|
|
20
|
-
abstractChainId,
|
|
21
|
-
berachainChainId,
|
|
22
16
|
unichainChainId,
|
|
23
|
-
soneiumChainId,
|
|
24
17
|
hyperEvmChainId,
|
|
25
18
|
katanaChainId,
|
|
26
19
|
plasmaChainId,
|
|
@@ -51,8 +44,6 @@ export const SYMBIOSIS_MAX_SLIPPAGE_BPS = 1000;
|
|
|
51
44
|
export const SYMBIOSIS_PARTNER_ADDRESS = '0xF5AA59151bE6515C4Ca68A0282CF68B3eA4846fC';
|
|
52
45
|
export const SYMBIOSIS_PARTNER_FEE_BPS = '60';
|
|
53
46
|
export const SYMBIOSIS_PARTNER_FEE_DESCRIPTION = 'Partner fee';
|
|
54
|
-
// Sent as X-Partner-Id so Symbiosis attributes our swaps to this client id in its explorer and metrics
|
|
55
|
-
export const SYMBIOSIS_PARTNER_ID = 'shapeshift';
|
|
56
47
|
export const DEFAULT_SYMBIOSIS_EVM_USER_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
|
|
57
48
|
export const DEFAULT_SYMBIOSIS_TRON_USER_ADDRESS = 'TLa2f6VPqDgRE67v1736s7bJ8Ray5wYjU7';
|
|
58
49
|
// metaRoute energy over 31 mainnet calls on 2026-10-05: up to 467,826 bridging the sell token, up to 578,456 with a source swap first
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import { makeSwapperAxiosServiceMonadic } from '../../../utils/index.js';
|
|
3
|
-
import { SYMBIOSIS_PARTNER_ID } from './constants.js';
|
|
4
3
|
const symbiosisServiceBase = axios.create({
|
|
5
|
-
timeout:
|
|
4
|
+
timeout: 15_000,
|
|
6
5
|
headers: {
|
|
7
6
|
Accept: 'application/json',
|
|
8
7
|
'Content-Type': 'application/json',
|
|
9
|
-
'X-Partner-Id': SYMBIOSIS_PARTNER_ID,
|
|
10
8
|
},
|
|
11
9
|
});
|
|
12
10
|
export const symbiosisService = makeSwapperAxiosServiceMonadic(symbiosisServiceBase);
|
|
@@ -3,7 +3,7 @@ import { createCache, makeSwapperAxiosServiceMonadic } from '../../../utils/inde
|
|
|
3
3
|
const maxAge = 5 * 1000; // 5 seconds
|
|
4
4
|
const cachedUrls = ['/swap/v1/price', '/swap/permit2/price'];
|
|
5
5
|
const axiosConfig = {
|
|
6
|
-
timeout:
|
|
6
|
+
timeout: 15_000,
|
|
7
7
|
};
|
|
8
8
|
export const zrxServiceFactory = ({ baseUrl, wrapper = identity, }) => {
|
|
9
9
|
const cache = createCache(maxAge, cachedUrls, { ...axiosConfig, baseURL: baseUrl });
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export type SwapperConfig = {
|
|
|
59
59
|
VITE_DEBRIDGE_API_URL: string;
|
|
60
60
|
VITE_BOB_GATEWAY_API_KEY: string;
|
|
61
61
|
VITE_SYMBIOSIS_API_URL: string;
|
|
62
|
+
VITE_PANORA_API_KEY: string;
|
|
62
63
|
};
|
|
63
64
|
export declare enum SwapperName {
|
|
64
65
|
Thorchain = "THORChain",
|
|
@@ -82,7 +83,8 @@ export declare enum SwapperName {
|
|
|
82
83
|
BobGateway = "BOB Gateway",
|
|
83
84
|
Symbiosis = "Symbiosis",
|
|
84
85
|
Fynd = "Fynd",
|
|
85
|
-
KyberSwap = "KyberSwap"
|
|
86
|
+
KyberSwap = "KyberSwap",
|
|
87
|
+
Panora = "Panora"
|
|
86
88
|
}
|
|
87
89
|
export type SwapSource = SwapperName | `${SwapperName} • ${string}`;
|
|
88
90
|
export type SwapErrorRight = {
|
package/dist/esm/types.js
CHANGED
|
@@ -22,6 +22,7 @@ export var SwapperName;
|
|
|
22
22
|
SwapperName["Symbiosis"] = "Symbiosis";
|
|
23
23
|
SwapperName["Fynd"] = "Fynd";
|
|
24
24
|
SwapperName["KyberSwap"] = "KyberSwap";
|
|
25
|
+
SwapperName["Panora"] = "Panora";
|
|
25
26
|
})(SwapperName || (SwapperName = {}));
|
|
26
27
|
export var TradeQuoteError;
|
|
27
28
|
(function (TradeQuoteError) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { aptosChainId } from '@shapeshiftoss/caip';
|
|
2
|
+
import { TxStatus } from '@shapeshiftoss/unchained-client';
|
|
3
|
+
import { createDefaultStatusResponse } from '../../utils/index.js';
|
|
4
|
+
export const checkAptosSwapStatus = async ({ txHash, assertGetAptosChainAdapter, }) => {
|
|
5
|
+
try {
|
|
6
|
+
const adapter = assertGetAptosChainAdapter(aptosChainId);
|
|
7
|
+
const tx = await adapter.getAptosClient().getTransactionByHash({ transactionHash: txHash });
|
|
8
|
+
if (tx.type === 'pending_transaction') {
|
|
9
|
+
return { status: TxStatus.Pending, buyTxHash: undefined, message: undefined };
|
|
10
|
+
}
|
|
11
|
+
if (tx.type !== 'user_transaction')
|
|
12
|
+
return createDefaultStatusResponse(txHash);
|
|
13
|
+
return {
|
|
14
|
+
status: tx.success ? TxStatus.Confirmed : TxStatus.Failed,
|
|
15
|
+
buyTxHash: txHash,
|
|
16
|
+
message: tx.success ? undefined : tx.vm_status,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.error(e);
|
|
21
|
+
return createDefaultStatusResponse(txHash);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { aptos } from '@shapeshiftoss/chain-adapters';
|
|
2
|
+
type GetAptosNetworkFeeCryptoBaseUnitArgs = {
|
|
3
|
+
adapter: aptos.ChainAdapter;
|
|
4
|
+
from: string;
|
|
5
|
+
payload: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const getAptosNetworkFeeCryptoBaseUnit: ({ adapter, from, payload, }: GetAptosNetworkFeeCryptoBaseUnitArgs) => Promise<{
|
|
8
|
+
networkFeeCryptoBaseUnit: string;
|
|
9
|
+
feeData: import("@shapeshiftoss/chain-adapters").FeeData<import("@shapeshiftoss/types").KnownChainIds.AptosMainnet>;
|
|
10
|
+
}>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const getAptosNetworkFeeCryptoBaseUnit = async ({ adapter, from, payload, }) => {
|
|
2
|
+
const { fast } = await adapter.getFeeData({
|
|
3
|
+
to: '',
|
|
4
|
+
value: '0',
|
|
5
|
+
chainSpecific: { from, payload },
|
|
6
|
+
});
|
|
7
|
+
return { networkFeeCryptoBaseUnit: fast.txFee, feeData: fast };
|
|
8
|
+
};
|
|
@@ -25,7 +25,6 @@ export const SUPPORTED_CHAIN_IDS = [
|
|
|
25
25
|
KnownChainIds.PolygonMainnet,
|
|
26
26
|
KnownChainIds.BnbSmartChainMainnet,
|
|
27
27
|
KnownChainIds.PlasmaMainnet,
|
|
28
|
-
KnownChainIds.LineaMainnet,
|
|
29
28
|
KnownChainIds.InkMainnet,
|
|
30
29
|
];
|
|
31
30
|
export const COW_SWAP_VAULT_RELAYER_ADDRESS = '0xc92e8bdf79f0507f65a392b0ab4667716bfe0110';
|
|
@@ -26,8 +26,6 @@ export const getCowNetwork = (chainId) => {
|
|
|
26
26
|
return CowNetwork.Bnb;
|
|
27
27
|
case KnownChainIds.PlasmaMainnet:
|
|
28
28
|
return CowNetwork.Plasma;
|
|
29
|
-
case KnownChainIds.LineaMainnet:
|
|
30
|
-
return CowNetwork.Linea;
|
|
31
29
|
case KnownChainIds.InkMainnet:
|
|
32
30
|
return CowNetwork.Ink;
|
|
33
31
|
default:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CHAIN_NAMESPACE, fromAssetId } from '@shapeshiftoss/caip';
|
|
2
2
|
import { KnownChainIds } from '@shapeshiftoss/types';
|
|
3
|
-
import { chainIdToFeeAssetId, DAO_TREASURY_ARBITRUM, DAO_TREASURY_AVALANCHE, DAO_TREASURY_BASE, DAO_TREASURY_BITCOIN, DAO_TREASURY_BOB, DAO_TREASURY_BSC, DAO_TREASURY_ELYSIUM, DAO_TREASURY_ETHEREUM_MAINNET, DAO_TREASURY_GNOSIS, DAO_TREASURY_HYPEREVM, DAO_TREASURY_MEGAETH, DAO_TREASURY_MONAD, DAO_TREASURY_OPTIMISM, DAO_TREASURY_PLASMA, DAO_TREASURY_POLYGON, DAO_TREASURY_ROBINHOOD, DAO_TREASURY_SOLANA, DAO_TREASURY_STARKNET, DAO_TREASURY_TON, DAO_TREASURY_UNICHAIN, isTreasuryChainId, } from '@shapeshiftoss/utils';
|
|
3
|
+
import { chainIdToFeeAssetId, DAO_TREASURY_APTOS, DAO_TREASURY_ARBITRUM, DAO_TREASURY_AVALANCHE, DAO_TREASURY_BASE, DAO_TREASURY_BITCOIN, DAO_TREASURY_BOB, DAO_TREASURY_BSC, DAO_TREASURY_ELYSIUM, DAO_TREASURY_ETHEREUM_MAINNET, DAO_TREASURY_GNOSIS, DAO_TREASURY_HYPEREVM, DAO_TREASURY_MEGAETH, DAO_TREASURY_MONAD, DAO_TREASURY_OPTIMISM, DAO_TREASURY_PLASMA, DAO_TREASURY_POLYGON, DAO_TREASURY_ROBINHOOD, DAO_TREASURY_SOLANA, DAO_TREASURY_STARKNET, DAO_TREASURY_TON, DAO_TREASURY_UNICHAIN, isTreasuryChainId, } from '@shapeshiftoss/utils';
|
|
4
4
|
import { SwapperName } from '../types.js';
|
|
5
5
|
export const getTradeAmount = (input) => 'buyAmountCryptoBaseUnit' in input
|
|
6
6
|
? { direction: 'exactOut', cryptoBaseUnit: input.buyAmountCryptoBaseUnit }
|
|
@@ -40,6 +40,7 @@ const DAO_TREASURY_BY_CHAIN_ID = {
|
|
|
40
40
|
[KnownChainIds.MegaEthMainnet]: DAO_TREASURY_MEGAETH,
|
|
41
41
|
[KnownChainIds.UnichainMainnet]: DAO_TREASURY_UNICHAIN,
|
|
42
42
|
[KnownChainIds.ElysiumMainnet]: DAO_TREASURY_ELYSIUM,
|
|
43
|
+
[KnownChainIds.AptosMainnet]: DAO_TREASURY_APTOS,
|
|
43
44
|
};
|
|
44
45
|
export const getTreasuryAddressFromChainId = (chainId) => {
|
|
45
46
|
const maybeTreasuryChainId = isTreasuryChainId(chainId) ? chainId : undefined;
|
|
@@ -29,6 +29,7 @@ describe('getTreasuryAddressFromChainId', () => {
|
|
|
29
29
|
[KnownChainIds.SolanaMainnet]: 'FxXyPB5RH4uHLPPJR5H89zGwZp19juBetmRwrxfsLj2j',
|
|
30
30
|
[KnownChainIds.StarknetMainnet]: '0x07ac2252f2da7cbf085e7a5ddc1318243aa818607cdd430dd2e17dd5d487606a',
|
|
31
31
|
[KnownChainIds.TonMainnet]: 'UQAHHeOhXst-zSGGigQ8KgDzz89nACBR4TxXwXNjU4DsriLb',
|
|
32
|
+
[KnownChainIds.AptosMainnet]: '0x7b504ecec7f738a716803a676e6d5df04d30240dcb8e5ba9a3acd70d5d91c2d8',
|
|
32
33
|
};
|
|
33
34
|
it.each(treasuryChainIds)('returns the treasury address for %s', chainId => {
|
|
34
35
|
expect(getTreasuryAddressFromChainId(chainId)).toBe(expectedTreasuryAddressByChainId[chainId]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshiftoss/swapper",
|
|
3
|
-
"version": "20.6.
|
|
3
|
+
"version": "20.6.2",
|
|
4
4
|
"repository": "https://github.com/shapeshift/web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
"retry-axios": "^2.6.0",
|
|
50
50
|
"uuid": "^9.0.0",
|
|
51
51
|
"viem": "2.55.11",
|
|
52
|
-
"@shapeshiftoss/caip": "^8.19.
|
|
52
|
+
"@shapeshiftoss/caip": "^8.19.2",
|
|
53
|
+
"@shapeshiftoss/contracts": "^2.1.2",
|
|
54
|
+
"@shapeshiftoss/types": "^9.3.2",
|
|
53
55
|
"@shapeshiftoss/hdwallet-core": "^1.62.41",
|
|
54
|
-
"@shapeshiftoss/chain-adapters": "^11.9.
|
|
55
|
-
"@shapeshiftoss/types": "^9.3.0",
|
|
56
|
+
"@shapeshiftoss/chain-adapters": "^11.9.2",
|
|
56
57
|
"@shapeshiftoss/unchained-client": "^10.17.0",
|
|
57
|
-
"@shapeshiftoss/utils": "^2.1.
|
|
58
|
-
"@shapeshiftoss/contracts": "^2.1.0"
|
|
58
|
+
"@shapeshiftoss/utils": "^2.1.2"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/lodash": "^4.14.178",
|