@shapeshiftoss/swapper 9.0.1 → 9.2.0

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 (35) hide show
  1. package/dist/api.d.ts +0 -1
  2. package/dist/swappercli.js +0 -1
  3. package/dist/swappers/cow/types.d.ts +0 -4
  4. package/dist/swappers/cow/utils/constants.d.ts +2 -1
  5. package/dist/swappers/cow/utils/constants.js +3 -2
  6. package/dist/swappers/cow/utils/helpers/helpers.d.ts +8 -3
  7. package/dist/swappers/cow/utils/helpers/helpers.js +43 -17
  8. package/dist/swappers/thorchain/ThorchainSwapper.d.ts +3 -3
  9. package/dist/swappers/thorchain/ThorchainSwapper.js +47 -19
  10. package/dist/swappers/thorchain/buildThorTrade/buildThorTrade.d.ts +7 -0
  11. package/dist/swappers/thorchain/buildThorTrade/buildThorTrade.js +95 -0
  12. package/dist/swappers/thorchain/getThorTradeQuote/getTradeQuote.js +19 -24
  13. package/dist/swappers/thorchain/thorTradeApprovalNeeded/thorTradeApprovalNeeded.js +2 -6
  14. package/dist/swappers/thorchain/utils/bitcoin/utils/getThorTxData.d.ts +1 -0
  15. package/dist/swappers/thorchain/utils/bitcoin/utils/getThorTxData.js +10 -10
  16. package/dist/swappers/thorchain/utils/constants.d.ts +5 -0
  17. package/dist/swappers/thorchain/utils/constants.js +8 -1
  18. package/dist/swappers/thorchain/utils/estimateTradeFee/estimateTradeFee.d.ts +2 -2
  19. package/dist/swappers/thorchain/utils/estimateTradeFee/estimateTradeFee.js +28 -19
  20. package/dist/swappers/thorchain/utils/ethereum/makeTradeTx.d.ts +2 -1
  21. package/dist/swappers/thorchain/utils/ethereum/makeTradeTx.js +5 -3
  22. package/dist/swappers/thorchain/utils/ethereum/utils/getThorTxData.d.ts +1 -0
  23. package/dist/swappers/thorchain/utils/ethereum/utils/getThorTxData.js +10 -10
  24. package/dist/swappers/thorchain/utils/getTradeRate/getTradeRate.js +8 -0
  25. package/dist/swappers/thorchain/utils/test-data/setupThorswapDeps.js +19 -1
  26. package/dist/swappers/thorchain/utils/txFeeHelpers/btcTxFees/getBtcTxFees.d.ts +2 -5
  27. package/dist/swappers/thorchain/utils/txFeeHelpers/btcTxFees/getBtcTxFees.js +1 -3
  28. package/dist/swappers/thorchain/utils/txFeeHelpers/ethTxFees/getEthTxFees.d.ts +3 -10
  29. package/dist/swappers/thorchain/utils/txFeeHelpers/ethTxFees/getEthTxFees.js +14 -26
  30. package/dist/swappers/utils/test-data/setupSwapQuote.d.ts +0 -1
  31. package/dist/swappers/utils/test-data/setupSwapQuote.js +0 -1
  32. package/dist/swappers/zrx/utils/test-data/setupZrxSwapQuote.d.ts +0 -1
  33. package/dist/swappers/zrx/utils/test-data/setupZrxSwapQuote.js +0 -1
  34. package/dist/swappers/zrx/zrxBuildTrade/zrxBuildTrade.js +1 -3
  35. package/package.json +1 -1
package/dist/api.d.ts CHANGED
@@ -85,7 +85,6 @@ declare type GetBtcTradeQuoteInput = CommonTradeInput & {
85
85
  };
86
86
  export declare type GetTradeQuoteInput = GetBtcTradeQuoteInput | GetEvmTradeQuoteInput | GetCosmosSdkTradeQuoteInput;
87
87
  export declare type BuildTradeInput = GetTradeQuoteInput & {
88
- buyAssetAccountNumber: number;
89
88
  slippage?: string;
90
89
  wallet: HDWallet;
91
90
  };
@@ -137,7 +137,6 @@ const main = async () => {
137
137
  sellAmount: sellAmountBase,
138
138
  sellAsset,
139
139
  sellAssetAccountNumber: 0,
140
- buyAssetAccountNumber: 0,
141
140
  receiveAddress: '0xc770eefad204b5180df6a14ee197d99d808ee52d'
142
141
  });
143
142
  const txid = await swapper.executeTrade({ trade, wallet });
@@ -1,9 +1,5 @@
1
1
  import { ChainId } from '@shapeshiftoss/caip';
2
2
  import { Trade } from '../../api';
3
- export declare type CowSwapPriceResponse = {
4
- amount: string;
5
- token: string;
6
- };
7
3
  export declare type CowSwapQuoteResponse = {
8
4
  quote: {
9
5
  sellToken: string;
@@ -1,6 +1,6 @@
1
1
  export declare const MAX_ALLOWANCE = "100000000000000000000000000";
2
2
  export declare const MAX_COWSWAP_TRADE = "100000000000000000000000000";
3
- export declare const MIN_COWSWAP_VALUE_USD = 10;
3
+ export declare const MIN_COWSWAP_VALUE_USD = 20;
4
4
  export declare const DEFAULT_SOURCE: {
5
5
  name: string;
6
6
  proportion: string;
@@ -11,6 +11,7 @@ export declare const COW_SWAP_VAULT_RELAYER_ADDRESS = "0xc92e8bdf79f0507f65a392b
11
11
  export declare const COW_SWAP_SETTLEMENT_ADDRESS = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";
12
12
  export declare const WETH_ASSET_ID = "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
13
13
  export declare const ORDER_KIND_SELL = "sell";
14
+ export declare const ORDER_KIND_BUY = "buy";
14
15
  export declare const SIGNING_SCHEME = "ethsign";
15
16
  export declare const ERC20_TOKEN_BALANCE = "erc20";
16
17
  export declare const ORDER_STATUS_FULFILLED = "fulfilled";
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ORDER_STATUS_FULFILLED = exports.ERC20_TOKEN_BALANCE = exports.SIGNING_SCHEME = exports.ORDER_KIND_SELL = exports.WETH_ASSET_ID = exports.COW_SWAP_SETTLEMENT_ADDRESS = exports.COW_SWAP_VAULT_RELAYER_ADDRESS = exports.DEFAULT_APP_DATA = exports.DEFAULT_ADDRESS = exports.DEFAULT_SOURCE = exports.MIN_COWSWAP_VALUE_USD = exports.MAX_COWSWAP_TRADE = exports.MAX_ALLOWANCE = void 0;
3
+ exports.ORDER_STATUS_FULFILLED = exports.ERC20_TOKEN_BALANCE = exports.SIGNING_SCHEME = exports.ORDER_KIND_BUY = exports.ORDER_KIND_SELL = exports.WETH_ASSET_ID = exports.COW_SWAP_SETTLEMENT_ADDRESS = exports.COW_SWAP_VAULT_RELAYER_ADDRESS = exports.DEFAULT_APP_DATA = exports.DEFAULT_ADDRESS = exports.DEFAULT_SOURCE = exports.MIN_COWSWAP_VALUE_USD = exports.MAX_COWSWAP_TRADE = exports.MAX_ALLOWANCE = void 0;
4
4
  const constants_1 = require("@ethersproject/constants");
5
5
  exports.MAX_ALLOWANCE = '100000000000000000000000000';
6
6
  exports.MAX_COWSWAP_TRADE = '100000000000000000000000000';
7
- exports.MIN_COWSWAP_VALUE_USD = 10;
7
+ exports.MIN_COWSWAP_VALUE_USD = 20;
8
8
  exports.DEFAULT_SOURCE = [{ name: 'CowSwap', proportion: '1' }];
9
9
  exports.DEFAULT_ADDRESS = constants_1.AddressZero;
10
10
  exports.DEFAULT_APP_DATA = constants_1.HashZero;
@@ -12,6 +12,7 @@ exports.COW_SWAP_VAULT_RELAYER_ADDRESS = '0xc92e8bdf79f0507f65a392b0ab4667716bfe
12
12
  exports.COW_SWAP_SETTLEMENT_ADDRESS = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41';
13
13
  exports.WETH_ASSET_ID = 'eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
14
14
  exports.ORDER_KIND_SELL = 'sell';
15
+ exports.ORDER_KIND_BUY = 'buy';
15
16
  exports.SIGNING_SCHEME = 'ethsign';
16
17
  exports.ERC20_TOKEN_BALANCE = 'erc20';
17
18
  exports.ORDER_STATUS_FULFILLED = 'fulfilled';
@@ -23,19 +23,24 @@ export declare type CowSwapOrder = {
23
23
  sellTokenBalance: string;
24
24
  buyTokenBalance: string;
25
25
  };
26
- export declare type CowSwapQuoteApiInput = {
26
+ export declare type CowSwapQuoteApiInputBase = {
27
27
  appData: string;
28
28
  buyToken: string;
29
29
  from: string;
30
30
  kind: string;
31
31
  partiallyFillable: boolean;
32
32
  receiver: string;
33
- sellAmountBeforeFee: string;
34
33
  sellToken: string;
35
34
  validTo: number;
36
35
  };
37
- export declare const getUsdRate: ({ apiUrl }: CowSwapperDeps, input: Asset) => Promise<string>;
36
+ export declare type CowSwapSellQuoteApiInput = CowSwapQuoteApiInputBase & {
37
+ sellAmountBeforeFee: string;
38
+ };
39
+ export declare type CowSwapBuyQuoteApiInput = CowSwapQuoteApiInputBase & {
40
+ buyAmountAfterFee: string;
41
+ };
38
42
  export declare const getNowPlusThirtyMinutesTimestamp: () => number;
43
+ export declare const getUsdRate: ({ apiUrl }: CowSwapperDeps, input: Asset) => Promise<string>;
39
44
  export declare const hashTypedData: (domain: TypedDataDomain, types: TypedDataTypes, data: Record<string, unknown>) => string;
40
45
  /**
41
46
  * Compute the 32-byte signing hash for the specified order.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.domain = exports.hashOrder = exports.hashTypedData = exports.getNowPlusThirtyMinutesTimestamp = exports.getUsdRate = exports.ORDER_TYPE_FIELDS = void 0;
3
+ exports.domain = exports.hashOrder = exports.hashTypedData = exports.getUsdRate = exports.getNowPlusThirtyMinutesTimestamp = exports.ORDER_TYPE_FIELDS = void 0;
4
4
  const asset_service_1 = require("@shapeshiftoss/asset-service");
5
5
  const caip_1 = require("@shapeshiftoss/caip");
6
6
  const ethers_1 = require("ethers");
@@ -24,6 +24,12 @@ exports.ORDER_TYPE_FIELDS = [
24
24
  { name: 'sellTokenBalance', type: 'string' },
25
25
  { name: 'buyTokenBalance', type: 'string' }
26
26
  ];
27
+ const getNowPlusThirtyMinutesTimestamp = () => {
28
+ const ts = new Date();
29
+ ts.setMinutes(ts.getMinutes() + 30);
30
+ return Math.round(ts.getTime() / 1000);
31
+ };
32
+ exports.getNowPlusThirtyMinutesTimestamp = getNowPlusThirtyMinutesTimestamp;
27
33
  const getUsdRate = async ({ apiUrl }, input) => {
28
34
  // Replacing ETH by WETH specifically for CowSwap in order to get an usd rate when called with ETH as feeAsset
29
35
  const asset = input.assetId !== caip_1.ethAssetId ? input : new asset_service_1.AssetService().getAll()[constants_1.WETH_ASSET_ID];
@@ -37,20 +43,46 @@ const getUsdRate = async ({ apiUrl }, input) => {
37
43
  if (erc20Address === USDC_CONTRACT_ADDRESS) {
38
44
  return '1';
39
45
  }
46
+ // rate is imprecise for low $ values, hence asking for $1000
40
47
  const buyAmountInDollars = 1000;
41
- const buyAmount = (0, bignumber_1.bn)(buyAmountInDollars).times((0, bignumber_1.bn)(10).exponentiatedBy(USDC_ASSET_PRECISION));
48
+ const buyAmount = (0, bignumber_1.bn)(buyAmountInDollars)
49
+ .times((0, bignumber_1.bn)(10).exponentiatedBy(USDC_ASSET_PRECISION))
50
+ .toString();
42
51
  try {
43
- // rate is imprecise for low $ values, hence asking for $1000
44
- // cowSwap api used : markets/{baseToken}-{quoteToken}/{kind}/{amount}
45
- // It returns the estimated amount in quoteToken for either buying or selling amount of baseToken.
46
- const rateResponse = await cowService_1.cowService.get(`${apiUrl}/v1/markets/${USDC_CONTRACT_ADDRESS}-${erc20Address}/buy/${buyAmount}`);
47
- const tokenAmount = (0, bignumber_1.bnOrZero)(rateResponse.data.amount).div((0, bignumber_1.bn)(10).exponentiatedBy(asset.precision));
48
- if (!tokenAmount.gt(0))
49
- throw new api_1.SwapError('[getUsdRate] - Failed to get token amount', {
52
+ const apiInput = {
53
+ sellToken: erc20Address,
54
+ buyToken: USDC_CONTRACT_ADDRESS,
55
+ receiver: constants_1.DEFAULT_ADDRESS,
56
+ validTo: (0, exports.getNowPlusThirtyMinutesTimestamp)(),
57
+ appData: constants_1.DEFAULT_APP_DATA,
58
+ partiallyFillable: false,
59
+ from: constants_1.DEFAULT_ADDRESS,
60
+ kind: constants_1.ORDER_KIND_BUY,
61
+ buyAmountAfterFee: buyAmount
62
+ };
63
+ /**
64
+ * /v1/quote
65
+ * params: {
66
+ * sellToken: contract address of token to sell
67
+ * buyToken: contractAddress of token to buy
68
+ * receiver: receiver address can be defaulted to "0x0000000000000000000000000000000000000000"
69
+ * validTo: time duration during which quote is valid (eg : 1654851610 as timestamp)
70
+ * appData: appData for the CowSwap quote that can be used later, can be defaulted to "0x0000000000000000000000000000000000000000000000000000000000000000"
71
+ * partiallyFillable: false
72
+ * from: sender address can be defaulted to "0x0000000000000000000000000000000000000000"
73
+ * kind: "sell" or "buy"
74
+ * sellAmountBeforeFee / buyAmountAfterFee: amount in base unit
75
+ * }
76
+ */
77
+ const quoteResponse = await cowService_1.cowService.post(`${apiUrl}/v1/quote/`, apiInput);
78
+ const { data: { quote } } = quoteResponse;
79
+ const sellCryptoAmount = (0, bignumber_1.bn)(quote.sellAmount).div((0, bignumber_1.bn)(10).exponentiatedBy(asset.precision));
80
+ if (!sellCryptoAmount.gt(0))
81
+ throw new api_1.SwapError('[getUsdRate] - Failed to get sell token amount', {
50
82
  code: api_1.SwapErrorTypes.RESPONSE_ERROR
51
83
  });
52
- // dividing $1000 by amount of token received
53
- return (0, bignumber_1.bn)(buyAmountInDollars).dividedBy(tokenAmount).toString();
84
+ // dividing $1000 by amount of sell token received
85
+ return (0, bignumber_1.bn)(buyAmountInDollars).div(sellCryptoAmount).toString();
54
86
  }
55
87
  catch (e) {
56
88
  if (e instanceof api_1.SwapError)
@@ -62,12 +94,6 @@ const getUsdRate = async ({ apiUrl }, input) => {
62
94
  }
63
95
  };
64
96
  exports.getUsdRate = getUsdRate;
65
- const getNowPlusThirtyMinutesTimestamp = () => {
66
- const ts = new Date();
67
- ts.setMinutes(ts.getMinutes() + 30);
68
- return Math.round(ts.getTime() / 1000);
69
- };
70
- exports.getNowPlusThirtyMinutesTimestamp = getNowPlusThirtyMinutesTimestamp;
71
97
  const hashTypedData = (domain, types, data) => {
72
98
  return ethers_1.ethers.utils._TypedDataEncoder.hash(domain, types, data);
73
99
  };
@@ -1,7 +1,7 @@
1
1
  import { Asset } from '@shapeshiftoss/asset-service';
2
2
  import { AssetId, ChainId } from '@shapeshiftoss/caip';
3
3
  import { KnownChainIds } from '@shapeshiftoss/types';
4
- import { ApprovalNeededInput, ApprovalNeededOutput, ApproveInfiniteInput, BuyAssetBySellIdInput, ExecuteTradeInput, GetTradeQuoteInput, Swapper, SwapperType, Trade, TradeQuote, TradeResult, TradeTxs } from '../../api';
4
+ import { ApprovalNeededInput, ApprovalNeededOutput, ApproveInfiniteInput, BuildTradeInput, BuyAssetBySellIdInput, ExecuteTradeInput, GetTradeQuoteInput, Swapper, SwapperType, Trade, TradeQuote, TradeResult, TradeTxs } from '../../api';
5
5
  import { ThorchainSwapperDeps } from './types';
6
6
  export declare class ThorchainSwapper implements Swapper<ChainId> {
7
7
  readonly name = "Thorchain";
@@ -16,8 +16,8 @@ export declare class ThorchainSwapper implements Swapper<ChainId> {
16
16
  approveInfinite(input: ApproveInfiniteInput<KnownChainIds.EthereumMainnet>): Promise<string>;
17
17
  filterBuyAssetsBySellAssetId(args: BuyAssetBySellIdInput): AssetId[];
18
18
  filterAssetIdsBySellable(): AssetId[];
19
- buildTrade(): Promise<Trade<ChainId>>;
19
+ buildTrade(input: BuildTradeInput): Promise<Trade<ChainId>>;
20
20
  getTradeQuote(input: GetTradeQuoteInput): Promise<TradeQuote<ChainId>>;
21
21
  executeTrade(args: ExecuteTradeInput<ChainId>): Promise<TradeResult>;
22
- getTradeTxs(): Promise<TradeTxs>;
22
+ getTradeTxs(tradeResult: TradeResult): Promise<TradeTxs>;
23
23
  }
@@ -4,6 +4,7 @@ exports.ThorchainSwapper = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const types_1 = require("@shapeshiftoss/types");
6
6
  const api_1 = require("../../api");
7
+ const buildThorTrade_1 = require("./buildThorTrade/buildThorTrade");
7
8
  const getTradeQuote_1 = require("./getThorTradeQuote/getTradeQuote");
8
9
  const thorTradeApprovalNeeded_1 = require("./thorTradeApprovalNeeded/thorTradeApprovalNeeded");
9
10
  const thorTradeApproveInfinite_1 = require("./thorTradeApproveInfinite/thorTradeApproveInfinite");
@@ -13,8 +14,8 @@ class ThorchainSwapper {
13
14
  constructor(deps) {
14
15
  this.name = 'Thorchain';
15
16
  this.swapSupportedChainIds = {
16
- 'eip155:1': true,
17
- 'bip122:000000000019d6689c085ae165831e93': true
17
+ [types_1.KnownChainIds.EthereumMainnet]: true,
18
+ [types_1.KnownChainIds.BitcoinMainnet]: true
18
19
  };
19
20
  this.supportedAssetIds = [];
20
21
  this.deps = deps;
@@ -59,33 +60,60 @@ class ThorchainSwapper {
59
60
  filterAssetIdsBySellable() {
60
61
  return this.supportedAssetIds;
61
62
  }
62
- async buildTrade() {
63
- throw new Error('ThorchainSwapper: buildTrade unimplemented');
63
+ async buildTrade(input) {
64
+ return (0, buildThorTrade_1.buildTrade)({ deps: this.deps, input });
64
65
  }
65
66
  async getTradeQuote(input) {
66
67
  return (0, getTradeQuote_1.getThorTradeQuote)({ deps: this.deps, input });
67
68
  }
68
69
  async executeTrade(args) {
69
- const { trade, wallet } = args;
70
- const adapter = this.deps.adapterManager.get(trade.sellAsset.chainId);
71
- if (adapter && trade.sellAsset.chainId === types_1.KnownChainIds.EthereumMainnet) {
72
- const thorTradeEth = trade;
73
- const signedTx = await adapter.signTransaction({
74
- txToSign: thorTradeEth.txData,
75
- wallet
76
- });
77
- const txid = await adapter.broadcastTransaction(signedTx);
78
- return { tradeId: txid };
70
+ try {
71
+ const { trade, wallet } = args;
72
+ const adapter = this.deps.adapterManager.get(trade.sellAsset.chainId);
73
+ if (!adapter)
74
+ throw new api_1.SwapError('[executeTrade]: no adapter for sell asset chain id', {
75
+ code: api_1.SwapErrorTypes.SIGN_AND_BROADCAST_FAILED,
76
+ details: { chainId: trade.sellAsset.chainId },
77
+ fn: 'executeTrade'
78
+ });
79
+ if (trade.sellAsset.chainId === types_1.KnownChainIds.EthereumMainnet) {
80
+ const signedTx = await adapter.signTransaction({
81
+ txToSign: trade.txData,
82
+ wallet
83
+ });
84
+ const txid = await adapter.broadcastTransaction(signedTx);
85
+ return { tradeId: txid };
86
+ }
87
+ else if (trade.sellAsset.chainId === types_1.KnownChainIds.BitcoinMainnet) {
88
+ const signedTx = await adapter.signTransaction({
89
+ txToSign: trade.txData,
90
+ wallet
91
+ });
92
+ const txid = await adapter.broadcastTransaction(signedTx);
93
+ return { tradeId: txid };
94
+ }
95
+ else {
96
+ throw new api_1.SwapError('[executeTrade]: unsupported trade', {
97
+ code: api_1.SwapErrorTypes.SIGN_AND_BROADCAST_FAILED,
98
+ fn: 'executeTrade'
99
+ });
100
+ }
79
101
  }
80
- else {
81
- throw new api_1.SwapError('[executeTrade]: unsupported trade', {
102
+ catch (e) {
103
+ if (e instanceof api_1.SwapError)
104
+ throw e;
105
+ throw new api_1.SwapError('[executeTrade]: failed to sign or broadcast', {
82
106
  code: api_1.SwapErrorTypes.SIGN_AND_BROADCAST_FAILED,
83
- fn: 'executeTrade'
107
+ cause: e
84
108
  });
85
109
  }
86
110
  }
87
- async getTradeTxs() {
88
- throw new Error('ThorchainSwapper: executeTrade unimplemented');
111
+ async getTradeTxs(tradeResult) {
112
+ // TODO poll midgard for the correct buyTxid
113
+ return {
114
+ sellTxid: tradeResult.tradeId,
115
+ buyTxid: tradeResult.tradeId
116
+ };
89
117
  }
90
118
  }
91
119
  exports.ThorchainSwapper = ThorchainSwapper;
@@ -0,0 +1,7 @@
1
+ import { ChainId } from '@shapeshiftoss/caip';
2
+ import { BuildTradeInput } from '../../../api';
3
+ import { ThorchainSwapperDeps, ThorTrade } from '../types';
4
+ export declare const buildTrade: ({ deps, input }: {
5
+ deps: ThorchainSwapperDeps;
6
+ input: BuildTradeInput;
7
+ }) => Promise<ThorTrade<ChainId>>;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTrade = void 0;
4
+ const types_1 = require("@shapeshiftoss/types");
5
+ const api_1 = require("../../../api");
6
+ const constants_1 = require("../../utils/constants");
7
+ const getTradeQuote_1 = require("../getThorTradeQuote/getTradeQuote");
8
+ const getThorTxData_1 = require("../utils/bitcoin/utils/getThorTxData");
9
+ const makeTradeTx_1 = require("../utils/ethereum/makeTradeTx");
10
+ const buildTrade = async ({ deps, input }) => {
11
+ var _a, _b, _c, _d;
12
+ try {
13
+ const { buyAsset, sellAsset, sellAssetAccountNumber, sellAmount, wallet, slippage: slippageTolerance = constants_1.DEFAULT_SLIPPAGE, receiveAddress: destinationAddress } = input;
14
+ const quote = await (0, getTradeQuote_1.getThorTradeQuote)({ deps, input });
15
+ const sellAdapter = deps.adapterManager.get(sellAsset.chainId);
16
+ if (!sellAdapter)
17
+ throw new api_1.SwapError('[buildTrade]: unsupported sell asset', {
18
+ code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
19
+ fn: 'buildTrade',
20
+ details: { sellAsset }
21
+ });
22
+ const sellAssetBip44Params = sellAdapter.buildBIP44Params({
23
+ accountNumber: sellAssetAccountNumber
24
+ });
25
+ if (input.chainId === types_1.KnownChainIds.EthereumMainnet) {
26
+ const ethTradeTx = await (0, makeTradeTx_1.makeTradeTx)({
27
+ wallet,
28
+ slippageTolerance,
29
+ bip44Params: sellAssetBip44Params,
30
+ sellAsset,
31
+ buyAsset,
32
+ adapter: sellAdapter,
33
+ sellAmount,
34
+ destinationAddress,
35
+ deps,
36
+ gasPrice: (_b = (_a = quote.feeData.chainSpecific) === null || _a === void 0 ? void 0 : _a.gasPrice) !== null && _b !== void 0 ? _b : '0',
37
+ gasLimit: (_d = (_c = quote.feeData.chainSpecific) === null || _c === void 0 ? void 0 : _c.estimatedGas) !== null && _d !== void 0 ? _d : '0',
38
+ tradeFee: quote.feeData.tradeFee
39
+ });
40
+ return {
41
+ chainId: types_1.KnownChainIds.EthereumMainnet,
42
+ ...quote,
43
+ receiveAddress: destinationAddress,
44
+ txData: ethTradeTx.txToSign
45
+ };
46
+ }
47
+ else if (input.chainId === types_1.KnownChainIds.BitcoinMainnet) {
48
+ const { vault, opReturnData } = await (0, getThorTxData_1.getThorTxInfo)({
49
+ deps,
50
+ sellAsset,
51
+ buyAsset,
52
+ sellAmount,
53
+ slippageTolerance,
54
+ destinationAddress,
55
+ wallet,
56
+ bip44Params: sellAssetBip44Params,
57
+ accountType: input.accountType,
58
+ tradeFee: quote.feeData.tradeFee
59
+ });
60
+ const buildTxResponse = await sellAdapter.buildSendTransaction({
61
+ value: sellAmount,
62
+ wallet,
63
+ to: vault,
64
+ chainSpecific: {
65
+ accountType: input.accountType,
66
+ satoshiPerByte: quote.feeData.chainSpecific
67
+ .satsPerByte,
68
+ opReturnData
69
+ }
70
+ });
71
+ return {
72
+ chainId: types_1.KnownChainIds.BitcoinMainnet,
73
+ ...quote,
74
+ receiveAddress: destinationAddress,
75
+ txData: buildTxResponse.txToSign
76
+ };
77
+ }
78
+ else {
79
+ throw new api_1.SwapError('[buildTrade]: unsupported chain id', {
80
+ code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
81
+ fn: 'buildTrade',
82
+ details: { sellAsset }
83
+ });
84
+ }
85
+ }
86
+ catch (e) {
87
+ if (e instanceof api_1.SwapError)
88
+ throw e;
89
+ throw new api_1.SwapError('[buildTrade]: error building trade', {
90
+ code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
91
+ fn: 'buildTrade'
92
+ });
93
+ }
94
+ };
95
+ exports.buildTrade = buildTrade;
@@ -6,18 +6,17 @@ const types_1 = require("@shapeshiftoss/types");
6
6
  const api_1 = require("../../../api");
7
7
  const bignumber_1 = require("../../utils/bignumber");
8
8
  const constants_1 = require("../../utils/constants");
9
- const helpers_1 = require("../../utils/helpers/helpers");
10
9
  const getThorTxData_1 = require("../utils/bitcoin/utils/getThorTxData");
11
10
  const constants_2 = require("../utils/constants");
12
11
  const estimateTradeFee_1 = require("../utils/estimateTradeFee/estimateTradeFee");
13
12
  const getThorTxData_2 = require("../utils/ethereum/utils/getThorTxData");
14
- const getPriceRatio_1 = require("../utils/getPriceRatio/getPriceRatio");
13
+ const getTradeRate_1 = require("../utils/getTradeRate/getTradeRate");
15
14
  const getBtcTxFees_1 = require("../utils/txFeeHelpers/btcTxFees/getBtcTxFees");
16
15
  const getEthTxFees_1 = require("../utils/txFeeHelpers/ethTxFees/getEthTxFees");
17
16
  const getThorTradeQuote = async ({ deps, input }) => {
18
17
  const { sellAsset, buyAsset, sellAmount, sellAssetAccountNumber, wallet, chainId, receiveAddress } = input;
19
18
  if (!wallet)
20
- throw new api_1.SwapError('[getTradeQuote] - wallet is required', {
19
+ throw new api_1.SwapError('[getThorTradeQuote] - wallet is required', {
21
20
  code: api_1.SwapErrorTypes.VALIDATION_FAILED
22
21
  });
23
22
  try {
@@ -28,7 +27,6 @@ const getThorTradeQuote = async ({ deps, input }) => {
28
27
  code: api_1.SwapErrorTypes.UNSUPPORTED_CHAIN,
29
28
  details: { chainId }
30
29
  });
31
- const sellAssetId = sellAsset.assetId;
32
30
  const buyAssetId = buyAsset.assetId;
33
31
  const feeAssetId = (0, caip_1.getFeeAssetIdFromAssetId)(buyAssetId);
34
32
  if (!feeAssetId)
@@ -36,13 +34,14 @@ const getThorTradeQuote = async ({ deps, input }) => {
36
34
  code: api_1.SwapErrorTypes.VALIDATION_FAILED,
37
35
  details: { buyAssetId }
38
36
  });
39
- const priceRatio = await (0, getPriceRatio_1.getPriceRatio)(deps, { sellAssetId, buyAssetId });
40
- const rate = (0, bignumber_1.bnOrZero)(1).div(priceRatio).toString();
41
- const buyAmount = (0, helpers_1.normalizeAmount)((0, bignumber_1.bnOrZero)(sellAmount).times(rate));
42
- const tradeFee = await (0, estimateTradeFee_1.estimateTradeFee)(deps, buyAsset.assetId);
43
- const sellAssetTradeFee = (0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(tradeFee).times((0, bignumber_1.bnOrZero)(priceRatio)), buyAsset.precision);
44
- // padding minimum by 1.5 the trade fee to avoid thorchain "not enough to cover fee" errors.
45
- const minimum = (0, bignumber_1.bnOrZero)(sellAssetTradeFee).times(1.5).toString();
37
+ const tradeRate = await (0, getTradeRate_1.getTradeRate)(sellAsset, buyAsset.assetId, sellAmount, deps);
38
+ const rate = (0, bignumber_1.bnOrZero)(1).div(tradeRate).toString();
39
+ const buyAmount = (0, bignumber_1.toBaseUnit)((0, bignumber_1.bnOrZero)((0, bignumber_1.fromBaseUnit)(sellAmount, sellAsset.precision)).times(tradeRate), buyAsset.precision);
40
+ const tradeFee = await (0, estimateTradeFee_1.estimateTradeFee)(deps, buyAsset);
41
+ const sellAssetTradeFee = (0, bignumber_1.bnOrZero)(tradeFee).times((0, bignumber_1.bnOrZero)(rate));
42
+ // minimum is tradeFee padded by an amount to be sure they get something back
43
+ // usually it will be slightly more than the amount because sellAssetTradeFee is already a high estimate
44
+ const minimum = (0, bignumber_1.bnOrZero)(sellAssetTradeFee).times(constants_2.THOR_MINIMUM_PADDING).toString();
46
45
  const commonQuoteFields = {
47
46
  rate,
48
47
  maximum: constants_2.MAX_THORCHAIN_TRADE,
@@ -57,23 +56,19 @@ const getThorTradeQuote = async ({ deps, input }) => {
57
56
  switch (chainId) {
58
57
  case types_1.KnownChainIds.EthereumMainnet:
59
58
  return (async () => {
60
- const { data, router } = await (0, getThorTxData_2.getThorTxInfo)({
59
+ const { router } = await (0, getThorTxData_2.getThorTxInfo)({
61
60
  deps,
62
61
  sellAsset,
63
62
  buyAsset,
64
63
  sellAmount,
65
64
  slippageTolerance: constants_1.DEFAULT_SLIPPAGE,
66
- destinationAddress: receiveAddress
65
+ destinationAddress: receiveAddress,
66
+ tradeFee
67
67
  });
68
68
  const feeData = await (0, getEthTxFees_1.getEthTxFees)({
69
- deps,
70
- data,
71
- router,
72
- buyAsset,
73
- sellAmount,
74
69
  adapterManager: deps.adapterManager,
75
- receiveAddress,
76
- sellAssetReference: sellAssetErc20Address
70
+ sellAssetReference: sellAssetErc20Address,
71
+ tradeFee
77
72
  });
78
73
  return {
79
74
  ...commonQuoteFields,
@@ -92,16 +87,16 @@ const getThorTradeQuote = async ({ deps, input }) => {
92
87
  destinationAddress: receiveAddress,
93
88
  wallet,
94
89
  bip44Params: input.bip44Params,
95
- accountType: input.accountType
90
+ accountType: input.accountType,
91
+ tradeFee
96
92
  });
97
93
  const feeData = await (0, getBtcTxFees_1.getBtcTxFees)({
98
- deps,
99
- buyAsset,
100
94
  sellAmount,
101
95
  vault,
102
96
  opReturnData,
103
97
  pubkey,
104
- adapterManager: deps.adapterManager
98
+ adapterManager: deps.adapterManager,
99
+ tradeFee
105
100
  });
106
101
  return {
107
102
  ...commonQuoteFields,
@@ -14,12 +14,8 @@ const thorTradeApprovalNeeded = async ({ deps, input }) => {
14
14
  const { adapterManager, web3 } = deps;
15
15
  const { assetReference: sellAssetErc20Address } = (0, caip_1.fromAssetId)(sellAsset.assetId);
16
16
  const { chainNamespace } = (0, caip_1.fromChainId)(sellAsset.chainId);
17
- if (chainNamespace !== 'eip155') {
18
- throw new api_1.SwapError('[thorTradeApprovalNeeded] - sellAsset chain namespace is not supported', {
19
- code: api_1.SwapErrorTypes.UNSUPPORTED_CHAIN,
20
- details: { chainNamespace }
21
- });
22
- }
17
+ if (chainNamespace !== caip_1.CHAIN_NAMESPACE.Ethereum)
18
+ return { approvalNeeded: false };
23
19
  // No approval needed for selling a fee asset
24
20
  if (sellAsset.assetId === (0, caip_1.getFeeAssetIdFromAssetId)(sellAsset.assetId)) {
25
21
  return { approvalNeeded: false };
@@ -12,6 +12,7 @@ declare type GetBtcThorTxInfoArgs = {
12
12
  wallet: HDWallet;
13
13
  bip44Params: BIP44Params;
14
14
  accountType: UtxoAccountType;
15
+ tradeFee: string;
15
16
  };
16
17
  declare type GetBtcThorTxInfoReturn = Promise<{
17
18
  opReturnData: string;
@@ -4,10 +4,11 @@ exports.getThorTxInfo = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../../api");
6
6
  const bignumber_1 = require("../../../../utils/bignumber");
7
- const getPriceRatio_1 = require("../../getPriceRatio/getPriceRatio");
7
+ const constants_1 = require("../../constants");
8
+ const getTradeRate_1 = require("../../getTradeRate/getTradeRate");
8
9
  const makeSwapMemo_1 = require("../../makeSwapMemo/makeSwapMemo");
9
10
  const thorService_1 = require("../../thorService");
10
- const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTolerance, destinationAddress, wallet, bip44Params, accountType }) => {
11
+ const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTolerance, destinationAddress, wallet, bip44Params, accountType, tradeFee }) => {
11
12
  try {
12
13
  const { data: inboundAddresses } = await thorService_1.thorService.get(`${deps.midgardUrl}/thorchain/inbound_addresses`);
13
14
  const btcInboundAddresses = inboundAddresses.find((inbound) => inbound.chain === 'BTC');
@@ -17,19 +18,18 @@ const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTo
17
18
  code: api_1.SwapErrorTypes.RESPONSE_ERROR,
18
19
  details: { inboundAddresses }
19
20
  });
20
- const priceRatio = await (0, getPriceRatio_1.getPriceRatio)(deps, {
21
- buyAssetId: buyAsset.assetId,
22
- sellAssetId: sellAsset.assetId
23
- });
24
- const expectedBuyAmount = (0, bignumber_1.toBaseUnit)((0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(sellAmount).dividedBy(priceRatio), sellAsset.precision), buyAsset.precision);
21
+ const tradeRate = await (0, getTradeRate_1.getTradeRate)(sellAsset, buyAsset.assetId, sellAmount, deps);
22
+ const expectedBuyAmountPrecision8 = (0, bignumber_1.toBaseUnit)((0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(sellAmount).times(tradeRate), sellAsset.precision), constants_1.THORCHAIN_FIXED_PRECISION);
25
23
  const isValidSlippageRange = (0, bignumber_1.bnOrZero)(slippageTolerance).gte(0) && (0, bignumber_1.bnOrZero)(slippageTolerance).lte(1);
26
- if ((0, bignumber_1.bnOrZero)(expectedBuyAmount).lt(0) || !isValidSlippageRange)
24
+ if ((0, bignumber_1.bnOrZero)(expectedBuyAmountPrecision8).lt(0) || !isValidSlippageRange)
27
25
  throw new api_1.SwapError('[getThorTxInfo]: bad expected buy amount or bad slippage tolerance', {
28
26
  code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
29
- details: { expectedBuyAmount, slippageTolerance }
27
+ details: { expectedBuyAmountPrecision8, slippageTolerance }
30
28
  });
31
- const limit = (0, bignumber_1.bnOrZero)(expectedBuyAmount)
29
+ const tradeFeePrecision8 = (0, bignumber_1.toBaseUnit)((0, bignumber_1.bnOrZero)(tradeFee), constants_1.THORCHAIN_FIXED_PRECISION);
30
+ const limit = (0, bignumber_1.bnOrZero)(expectedBuyAmountPrecision8)
32
31
  .times((0, bignumber_1.bn)(1).minus(slippageTolerance))
32
+ .minus((0, bignumber_1.bnOrZero)(tradeFeePrecision8))
33
33
  .decimalPlaces(0)
34
34
  .toString();
35
35
  const memo = (0, makeSwapMemo_1.makeSwapMemo)({
@@ -1,2 +1,7 @@
1
1
  export declare const MAX_THORCHAIN_TRADE = "100000000000000000000000000";
2
2
  export declare const MAX_ALLOWANCE = "100000000000000000000000000";
3
+ export declare const THOR_MINIMUM_PADDING = 1.2;
4
+ export declare const THOR_ETH_GAS_LIMIT = "100000";
5
+ export declare const THORCHAIN_FIXED_PRECISION = 8;
6
+ export declare const THOR_TRADE_FEE_BTC_SIZE = 1000;
7
+ export declare const THOR_TRADE_FEE_ETH_GAS = 120000;
@@ -1,5 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_ALLOWANCE = exports.MAX_THORCHAIN_TRADE = void 0;
3
+ exports.THOR_TRADE_FEE_ETH_GAS = exports.THOR_TRADE_FEE_BTC_SIZE = exports.THORCHAIN_FIXED_PRECISION = exports.THOR_ETH_GAS_LIMIT = exports.THOR_MINIMUM_PADDING = exports.MAX_ALLOWANCE = exports.MAX_THORCHAIN_TRADE = void 0;
4
4
  exports.MAX_THORCHAIN_TRADE = '100000000000000000000000000';
5
5
  exports.MAX_ALLOWANCE = '100000000000000000000000000';
6
+ exports.THOR_MINIMUM_PADDING = 1.2;
7
+ exports.THOR_ETH_GAS_LIMIT = '100000'; // for sends of eth / erc20 into thorchain router
8
+ exports.THORCHAIN_FIXED_PRECISION = 8; // limit values are precision 8 regardless of the chain
9
+ // These are different from THOR_ETH_GAS_LIMIT
10
+ // Used to estimate the fee thorchain will take out of the buyAsset
11
+ exports.THOR_TRADE_FEE_BTC_SIZE = 1000; // used for estimating thorchain side fees
12
+ exports.THOR_TRADE_FEE_ETH_GAS = 120000; // used for estimating thorchain side fees
@@ -1,3 +1,3 @@
1
- import { AssetId } from '@shapeshiftoss/caip';
1
+ import { Asset } from '@shapeshiftoss/asset-service';
2
2
  import { ThorchainSwapperDeps } from '../../types';
3
- export declare const estimateTradeFee: (deps: ThorchainSwapperDeps, buyAssetId: AssetId) => Promise<string>;
3
+ export declare const estimateTradeFee: (deps: ThorchainSwapperDeps, buyAsset: Asset) => Promise<string>;
@@ -4,23 +4,32 @@ exports.estimateTradeFee = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../api");
6
6
  const bignumber_1 = require("../../../utils/bignumber");
7
+ const constants_1 = require("../constants");
7
8
  const getPriceRatio_1 = require("../getPriceRatio/getPriceRatio");
8
9
  const thorService_1 = require("../thorService");
9
10
  const gweiGasPrecision = 9;
10
- // https://docs.thorchain.org/how-it-works/fees#outbound-fee
11
- // estimatedTradeFee = `3 * estimatedFeeOutboundFee`
12
- // eth txs from thorchain use approx 38383 gas
13
- const ethEstimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate).times(38383).times(3).times((0, bignumber_1.bn)(10).exponentiatedBy(gweiGasPrecision)).toString();
14
- // erc20 txs from thorchain use approx 70996 gas
15
- const erc20Estimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate).times(70996).times(3).times((0, bignumber_1.bn)(10).exponentiatedBy(gweiGasPrecision)).toString();
16
- // BTC utxo transactions from thorchain are approx 303 bytes long
17
- const btcEstimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate).times(303).times(3).toString();
18
- const estimateTradeFee = async (deps, buyAssetId) => {
19
- const thorId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAssetId });
11
+ // Official docs on fees are incorrect
12
+ // https://discord.com/channels/838986635756044328/997675038675316776/998552541170253834
13
+ // This is still not "perfect" and tends to overestimate by a randomish amount
14
+ // TODO figure out if its possible to accurately estimate the outbound fee.
15
+ // Neither the discord nor official docs are correct
16
+ const ethEstimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate)
17
+ .times(constants_1.THOR_TRADE_FEE_ETH_GAS)
18
+ .times(2)
19
+ .times((0, bignumber_1.bn)(10).exponentiatedBy(gweiGasPrecision))
20
+ .toString();
21
+ const erc20Estimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate)
22
+ .times(constants_1.THOR_TRADE_FEE_ETH_GAS)
23
+ .times(2)
24
+ .times((0, bignumber_1.bn)(10).exponentiatedBy(gweiGasPrecision))
25
+ .toString();
26
+ const btcEstimate = (gasRate) => (0, bignumber_1.bnOrZero)(gasRate).times(constants_1.THOR_TRADE_FEE_BTC_SIZE).times(2).toString();
27
+ const estimateTradeFee = async (deps, buyAsset) => {
28
+ const thorId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAsset.assetId });
20
29
  if (!thorId)
21
30
  throw new api_1.SwapError('[estimateTradeFee] - undefined thorId for given buyAssetId', {
22
31
  code: api_1.SwapErrorTypes.VALIDATION_FAILED,
23
- details: { buyAssetId }
32
+ details: { buyAssetId: buyAsset.assetId }
24
33
  });
25
34
  const thorPoolChainId = thorId.slice(0, thorId.indexOf('.'));
26
35
  const { data: inboundAddresses } = await thorService_1.thorService.get(`${deps.midgardUrl}/thorchain/inbound_addresses`);
@@ -31,28 +40,28 @@ const estimateTradeFee = async (deps, buyAssetId) => {
31
40
  details: { thorPoolChainId }
32
41
  });
33
42
  const gasRate = inboundInfo.gas_rate;
34
- const { chainId, assetNamespace } = (0, caip_1.fromAssetId)(buyAssetId);
35
- const feeAssetId = (0, caip_1.getFeeAssetIdFromAssetId)(buyAssetId);
43
+ const { chainId, assetNamespace } = (0, caip_1.fromAssetId)(buyAsset.assetId);
44
+ const feeAssetId = (0, caip_1.getFeeAssetIdFromAssetId)(buyAsset.assetId);
36
45
  if (!feeAssetId)
37
46
  throw new api_1.SwapError('[estimateTradeFee] - no fee assetId', {
38
47
  code: api_1.SwapErrorTypes.VALIDATION_FAILED,
39
- details: { buyAssetId }
48
+ details: { buyAssetId: buyAsset.assetId }
40
49
  });
41
- const feeAssetRatio = buyAssetId !== feeAssetId
50
+ const feeAssetRatio = buyAsset.assetId !== feeAssetId
42
51
  ? await (0, getPriceRatio_1.getPriceRatio)(deps, {
43
- sellAssetId: buyAssetId,
52
+ sellAssetId: buyAsset.assetId,
44
53
  buyAssetId: feeAssetId
45
54
  })
46
55
  : '1';
47
56
  switch (chainId) {
48
57
  case 'bip122:000000000019d6689c085ae165831e93':
49
- return (0, bignumber_1.bnOrZero)(btcEstimate(gasRate)).times(feeAssetRatio).dp(0).toString();
58
+ return (0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(btcEstimate(gasRate)).times(feeAssetRatio).dp(0), buyAsset.precision);
50
59
  case 'eip155:1':
51
60
  switch (assetNamespace) {
52
61
  case 'slip44':
53
- return (0, bignumber_1.bnOrZero)(ethEstimate(gasRate)).times(feeAssetRatio).dp(0).toString();
62
+ return (0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(ethEstimate(gasRate)).times(feeAssetRatio).dp(0), buyAsset.precision);
54
63
  case 'erc20':
55
- return (0, bignumber_1.bnOrZero)(erc20Estimate(gasRate)).times(feeAssetRatio).dp(0).toString();
64
+ return (0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(erc20Estimate(gasRate)).times(feeAssetRatio).dp(0), buyAsset.precision);
56
65
  default:
57
66
  throw new api_1.SwapError('[estimateTradeFee] - unsupported asset namespace', {
58
67
  code: api_1.SwapErrorTypes.VALIDATION_FAILED,
@@ -3,7 +3,7 @@ import { ethereum } from '@shapeshiftoss/chain-adapters';
3
3
  import { ETHSignTx, HDWallet } from '@shapeshiftoss/hdwallet-core';
4
4
  import { BIP44Params } from '@shapeshiftoss/types';
5
5
  import { ThorchainSwapperDeps } from '../../types';
6
- export declare const makeTradeTx: ({ wallet, bip44Params, sellAmount, buyAsset, sellAsset, destinationAddress, adapter, maxFeePerGas, maxPriorityFeePerGas, gasPrice, slippageTolerance, deps, gasLimit }: {
6
+ export declare const makeTradeTx: ({ wallet, bip44Params, sellAmount, buyAsset, sellAsset, destinationAddress, adapter, maxFeePerGas, maxPriorityFeePerGas, gasPrice, slippageTolerance, deps, gasLimit, tradeFee }: {
7
7
  wallet: HDWallet;
8
8
  bip44Params: BIP44Params;
9
9
  sellAmount: string;
@@ -14,6 +14,7 @@ export declare const makeTradeTx: ({ wallet, bip44Params, sellAmount, buyAsset,
14
14
  slippageTolerance: string;
15
15
  deps: ThorchainSwapperDeps;
16
16
  gasLimit: string;
17
+ tradeFee: string;
17
18
  } & ({
18
19
  gasPrice: string;
19
20
  maxFeePerGas?: undefined;
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeTradeTx = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
+ const web3_utils_1 = require("web3-utils");
5
6
  const api_1 = require("../../../../api");
6
7
  const getThorTxData_1 = require("../ethereum/utils/getThorTxData");
7
- const makeTradeTx = async ({ wallet, bip44Params, sellAmount, buyAsset, sellAsset, destinationAddress, adapter, maxFeePerGas, maxPriorityFeePerGas, gasPrice, slippageTolerance, deps, gasLimit }) => {
8
+ const makeTradeTx = async ({ wallet, bip44Params, sellAmount, buyAsset, sellAsset, destinationAddress, adapter, maxFeePerGas, maxPriorityFeePerGas, gasPrice, slippageTolerance, deps, gasLimit, tradeFee }) => {
8
9
  try {
9
10
  const { assetNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
10
11
  const isErc20Trade = assetNamespace === 'erc20';
@@ -14,7 +15,8 @@ const makeTradeTx = async ({ wallet, bip44Params, sellAmount, buyAsset, sellAsse
14
15
  buyAsset,
15
16
  sellAmount,
16
17
  slippageTolerance,
17
- destinationAddress
18
+ destinationAddress,
19
+ tradeFee
18
20
  });
19
21
  return adapter.buildCustomTx({
20
22
  wallet,
@@ -29,7 +31,7 @@ const makeTradeTx = async ({ wallet, bip44Params, sellAmount, buyAsset, sellAsse
29
31
  maxFeePerGas,
30
32
  maxPriorityFeePerGas
31
33
  }),
32
- value: isErc20Trade ? '0' : sellAmount,
34
+ value: isErc20Trade ? '0x0' : (0, web3_utils_1.numberToHex)(sellAmount),
33
35
  data
34
36
  });
35
37
  }
@@ -7,6 +7,7 @@ declare type GetBtcThorTxInfoArgs = {
7
7
  sellAmount: string;
8
8
  slippageTolerance: string;
9
9
  destinationAddress: string;
10
+ tradeFee: string;
10
11
  };
11
12
  declare type GetBtcThorTxInfoReturn = Promise<{
12
13
  data: string;
@@ -4,11 +4,12 @@ exports.getThorTxInfo = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../../api");
6
6
  const bignumber_1 = require("../../../../utils/bignumber");
7
- const getPriceRatio_1 = require("../../getPriceRatio/getPriceRatio");
7
+ const getTradeRate_1 = require("../../../utils/getTradeRate/getTradeRate");
8
+ const constants_1 = require("../../constants");
8
9
  const makeSwapMemo_1 = require("../../makeSwapMemo/makeSwapMemo");
9
10
  const thorService_1 = require("../../thorService");
10
11
  const routerCalldata_1 = require("../routerCalldata");
11
- const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTolerance, destinationAddress }) => {
12
+ const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTolerance, destinationAddress, tradeFee }) => {
12
13
  try {
13
14
  const { assetReference, assetNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
14
15
  const isErc20Trade = assetNamespace === 'erc20';
@@ -21,19 +22,18 @@ const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTo
21
22
  code: api_1.SwapErrorTypes.RESPONSE_ERROR,
22
23
  details: { inboundAddresses }
23
24
  });
24
- const priceRatio = await (0, getPriceRatio_1.getPriceRatio)(deps, {
25
- buyAssetId: buyAsset.assetId,
26
- sellAssetId: sellAsset.assetId
27
- });
28
- const expectedBuyAmount = (0, bignumber_1.toBaseUnit)((0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(sellAmount).dividedBy(priceRatio), sellAsset.precision), buyAsset.precision);
25
+ const tradeRate = await (0, getTradeRate_1.getTradeRate)(sellAsset, buyAsset.assetId, sellAmount, deps);
26
+ const expectedBuyAmountPrecision8 = (0, bignumber_1.toBaseUnit)((0, bignumber_1.fromBaseUnit)((0, bignumber_1.bnOrZero)(sellAmount).times(tradeRate), sellAsset.precision), constants_1.THORCHAIN_FIXED_PRECISION);
27
+ const tradeFeePrecision8 = (0, bignumber_1.toBaseUnit)((0, bignumber_1.bnOrZero)(tradeFee), constants_1.THORCHAIN_FIXED_PRECISION);
29
28
  const isValidSlippageRange = (0, bignumber_1.bnOrZero)(slippageTolerance).gte(0) && (0, bignumber_1.bnOrZero)(slippageTolerance).lte(1);
30
- if ((0, bignumber_1.bnOrZero)(expectedBuyAmount).lt(0) || !isValidSlippageRange)
29
+ if ((0, bignumber_1.bnOrZero)(expectedBuyAmountPrecision8).lt(0) || !isValidSlippageRange)
31
30
  throw new api_1.SwapError('[makeTradeTx]: bad expected buy amount or bad slippage tolerance', {
32
31
  code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
33
- details: { expectedBuyAmount, slippageTolerance }
32
+ details: { expectedBuyAmountPrecision8, slippageTolerance }
34
33
  });
35
- const limit = (0, bignumber_1.bnOrZero)(expectedBuyAmount)
34
+ const limit = (0, bignumber_1.bnOrZero)(expectedBuyAmountPrecision8)
36
35
  .times((0, bignumber_1.bn)(1).minus(slippageTolerance))
36
+ .minus((0, bignumber_1.bnOrZero)(tradeFeePrecision8))
37
37
  .decimalPlaces(0)
38
38
  .toString();
39
39
  const memo = (0, makeSwapMemo_1.makeSwapMemo)({
@@ -5,6 +5,7 @@ const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../api");
6
6
  const bignumber_1 = require("../../../utils/bignumber");
7
7
  const bignumber_2 = require("../../../utils/bignumber");
8
+ const getPriceRatio_1 = require("../getPriceRatio/getPriceRatio");
8
9
  const thorService_1 = require("../thorService");
9
10
  const THOR_PRECISION = 8;
10
11
  const getSwapOutput = (inputAmount, poolData, toRune) => {
@@ -23,6 +24,13 @@ const getDoubleSwapOutput = (input, inputPool, outputPool) => {
23
24
  // TODO this does not support swaps between native "RUNE"
24
25
  // Rune swaps use a different calculation because its 1 hop between pools instead of 2
25
26
  const getTradeRate = async (sellAsset, buyAssetId, sellAmount, deps) => {
27
+ // we can't get a quote for a zero amount so use getPriceRatio between pools instead
28
+ if ((0, bignumber_1.bnOrZero)(sellAmount).eq(0)) {
29
+ return (0, getPriceRatio_1.getPriceRatio)(deps, {
30
+ sellAssetId: sellAsset.assetId,
31
+ buyAssetId
32
+ });
33
+ }
26
34
  const buyPoolId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAssetId });
27
35
  const sellPoolId = caip_1.adapters.assetIdToPoolAssetId({ assetId: sellAsset.assetId });
28
36
  if (!buyPoolId || !sellPoolId)
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.setupThorswapDeps = void 0;
7
+ const chain_adapters_1 = require("@shapeshiftoss/chain-adapters");
7
8
  const web3_1 = __importDefault(require("web3"));
8
9
  jest.mock('web3');
9
10
  const setupThorswapDeps = () => {
@@ -20,7 +21,24 @@ const setupThorswapDeps = () => {
20
21
  {
21
22
  buildBIP44Params: jest.fn(() => ({ purpose: 44, coinType: 60, accountNumber: 0 })),
22
23
  getAddress: jest.fn(() => Promise.resolve('0xthisIsMyAddress')),
23
- getFeeData: jest.fn(() => feeData)
24
+ getFeeData: jest.fn(() => feeData),
25
+ getGasFeeData: jest.fn(() => ({
26
+ [chain_adapters_1.FeeDataKey.Slow]: {
27
+ gasPrice: '1',
28
+ maxFeePerGas: '2',
29
+ maxPriorityFeePerGas: '3'
30
+ },
31
+ [chain_adapters_1.FeeDataKey.Average]: {
32
+ gasPrice: '4',
33
+ maxFeePerGas: '5',
34
+ maxPriorityFeePerGas: '6'
35
+ },
36
+ [chain_adapters_1.FeeDataKey.Fast]: {
37
+ gasPrice: '7',
38
+ maxFeePerGas: '8',
39
+ maxPriorityFeePerGas: '9'
40
+ }
41
+ }))
24
42
  }
25
43
  ]
26
44
  ]);
@@ -1,14 +1,11 @@
1
- import { Asset } from '@shapeshiftoss/asset-service';
2
1
  import { ChainAdapterManager } from '@shapeshiftoss/chain-adapters';
3
2
  import { KnownChainIds } from '@shapeshiftoss/types';
4
3
  import { QuoteFeeData } from '../../../../../api';
5
- import { ThorchainSwapperDeps } from '../../../types';
6
- export declare const getBtcTxFees: ({ deps, opReturnData, vault, buyAsset, sellAmount, adapterManager, pubkey }: {
7
- deps: ThorchainSwapperDeps;
4
+ export declare const getBtcTxFees: ({ opReturnData, vault, sellAmount, adapterManager, pubkey, tradeFee }: {
8
5
  opReturnData: string;
9
6
  vault: string;
10
- buyAsset: Asset;
11
7
  sellAmount: string;
12
8
  adapterManager: ChainAdapterManager;
13
9
  pubkey: string;
10
+ tradeFee: string;
14
11
  }) => Promise<QuoteFeeData<KnownChainIds.BitcoinMainnet>>;
@@ -3,8 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getBtcTxFees = void 0;
4
4
  const api_1 = require("../../../../../api");
5
5
  const bignumber_1 = require("../../../../utils/bignumber");
6
- const estimateTradeFee_1 = require("../../estimateTradeFee/estimateTradeFee");
7
- const getBtcTxFees = async ({ deps, opReturnData, vault, buyAsset, sellAmount, adapterManager, pubkey }) => {
6
+ const getBtcTxFees = async ({ opReturnData, vault, sellAmount, adapterManager, pubkey, tradeFee }) => {
8
7
  try {
9
8
  const adapter = adapterManager.get('bip122:000000000019d6689c085ae165831e93');
10
9
  if (!adapter)
@@ -18,7 +17,6 @@ const getBtcTxFees = async ({ deps, opReturnData, vault, buyAsset, sellAmount, a
18
17
  chainSpecific: { pubkey, opReturnData }
19
18
  });
20
19
  const feeData = feeDataOptions['fast'];
21
- const tradeFee = await (0, estimateTradeFee_1.estimateTradeFee)(deps, buyAsset.assetId);
22
20
  return {
23
21
  fee: feeData.txFee,
24
22
  tradeFee,
@@ -1,16 +1,9 @@
1
- import { Asset } from '@shapeshiftoss/asset-service';
2
1
  import { AssetReference } from '@shapeshiftoss/caip';
3
2
  import { ChainAdapterManager } from '@shapeshiftoss/chain-adapters';
4
3
  import { KnownChainIds } from '@shapeshiftoss/types';
5
4
  import { QuoteFeeData } from '../../../../../api';
6
- import { ThorchainSwapperDeps } from '../../../types';
7
- export declare const getEthTxFees: ({ deps, data, router, buyAsset, sellAmount, adapterManager, receiveAddress, sellAssetReference }: {
8
- deps: ThorchainSwapperDeps;
9
- data: string;
10
- router: string;
11
- buyAsset: Asset;
12
- sellAmount: string;
13
- sellAssetReference: AssetReference | string;
5
+ export declare const getEthTxFees: ({ adapterManager, sellAssetReference, tradeFee }: {
14
6
  adapterManager: ChainAdapterManager;
15
- receiveAddress: string;
7
+ sellAssetReference: AssetReference | string;
8
+ tradeFee: string;
16
9
  }) => Promise<QuoteFeeData<KnownChainIds.EthereumMainnet>>;
@@ -6,8 +6,8 @@ const types_1 = require("@shapeshiftoss/types");
6
6
  const api_1 = require("../../../../../api");
7
7
  const bignumber_1 = require("../../../../utils/bignumber");
8
8
  const constants_1 = require("../../../../utils/constants");
9
- const estimateTradeFee_1 = require("../../estimateTradeFee/estimateTradeFee");
10
- const getEthTxFees = async ({ deps, data, router, buyAsset, sellAmount, adapterManager, receiveAddress, sellAssetReference }) => {
9
+ const constants_2 = require("../../constants");
10
+ const getEthTxFees = async ({ adapterManager, sellAssetReference, tradeFee }) => {
11
11
  try {
12
12
  const adapter = adapterManager.get(types_1.KnownChainIds.EthereumMainnet);
13
13
  if (!adapter) {
@@ -16,32 +16,20 @@ const getEthTxFees = async ({ deps, data, router, buyAsset, sellAmount, adapterM
16
16
  details: { chainId: types_1.KnownChainIds.EthereumMainnet }
17
17
  });
18
18
  }
19
- let feeDataOptions;
20
- try {
21
- feeDataOptions = await adapter.getFeeData({
22
- to: router,
23
- value: sellAmount,
24
- chainSpecific: { from: receiveAddress, contractData: data }
25
- });
26
- }
27
- catch (e) {
28
- // Fallback to fixed fee amount in case of failure so that quote will not fail
29
- // eslint-disable-next-line no-console
30
- console.debug('[ThorSwapper:getEthTxFees] precise gas estimate failed, falling back on hard coded limit');
31
- const gasFeeData = await adapter.getGasFeeData();
32
- const gasLimit = '100000'; // good value to cover all thortrades out of eth/erc20
33
- feeDataOptions = {
34
- fast: {
35
- txFee: (0, bignumber_1.bn)(gasLimit).times(gasFeeData[chain_adapters_1.FeeDataKey.Fast].gasPrice).toString(),
36
- chainSpecific: {
37
- gasPrice: gasFeeData[chain_adapters_1.FeeDataKey.Fast].gasPrice,
38
- gasLimit
39
- }
19
+ const gasFeeData = await adapter.getGasFeeData();
20
+ // this is a good value to cover all thortrades out of eth/erc20
21
+ // in the future we may want to look at doing this more precisely and in a future-proof way
22
+ const gasLimit = constants_2.THOR_ETH_GAS_LIMIT;
23
+ const feeDataOptions = {
24
+ fast: {
25
+ txFee: (0, bignumber_1.bn)(gasLimit).times(gasFeeData[chain_adapters_1.FeeDataKey.Fast].gasPrice).toString(),
26
+ chainSpecific: {
27
+ gasPrice: gasFeeData[chain_adapters_1.FeeDataKey.Fast].gasPrice,
28
+ gasLimit
40
29
  }
41
- };
42
- }
30
+ }
31
+ };
43
32
  const feeData = feeDataOptions['fast'];
44
- const tradeFee = await (0, estimateTradeFee_1.estimateTradeFee)(deps, buyAsset.assetId);
45
33
  return {
46
34
  fee: feeData.txFee,
47
35
  chainSpecific: {
@@ -20,7 +20,6 @@ export declare const setupBuildTrade: () => {
20
20
  } & {
21
21
  chainId: import("../../../api").EvmSupportedChainIds;
22
22
  } & {
23
- buyAssetAccountNumber: number;
24
23
  slippage?: string | undefined;
25
24
  wallet: HDWallet;
26
25
  };
@@ -41,7 +41,6 @@ const setupBuildTrade = () => {
41
41
  buyAsset,
42
42
  sendMax: false,
43
43
  sellAssetAccountNumber: 0,
44
- buyAssetAccountNumber: 0,
45
44
  sellAsset,
46
45
  wallet: {},
47
46
  receiveAddress: ''
@@ -18,7 +18,6 @@ export declare const setupExecuteTrade: () => {
18
18
  sellAsset: Asset;
19
19
  sendMax: boolean;
20
20
  sellAssetAccountNumber: number;
21
- buyAssetAccountNumber: number;
22
21
  txData: string;
23
22
  depositAddress: string;
24
23
  receiveAddress: string;
@@ -34,7 +34,6 @@ const setupExecuteTrade = () => {
34
34
  sellAsset,
35
35
  sendMax: false,
36
36
  sellAssetAccountNumber: 0,
37
- buyAssetAccountNumber: 0,
38
37
  txData: '0x0',
39
38
  depositAddress: '0x0',
40
39
  receiveAddress: '0x0',
@@ -37,7 +37,7 @@ const helpers_2 = require("../utils/helpers/helpers");
37
37
  const zrxService_1 = require("../utils/zrxService");
38
38
  async function zrxBuildTrade({ adapter, web3 }, input) {
39
39
  var _a, _b, _c;
40
- const { sellAsset, buyAsset, sellAmount, slippage, sellAssetAccountNumber, buyAssetAccountNumber, wallet } = input;
40
+ const { sellAsset, buyAsset, sellAmount, slippage, sellAssetAccountNumber, receiveAddress } = input;
41
41
  try {
42
42
  const { assetReference: buyAssetErc20Address, assetNamespace: buyAssetNamespace } = (0, caip_1.fromAssetId)(buyAsset.assetId);
43
43
  const { assetReference: sellAssetErc20Address, assetNamespace: sellAssetNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
@@ -50,8 +50,6 @@ async function zrxBuildTrade({ adapter, web3 }, input) {
50
50
  details: { chainId: sellAsset.chainId }
51
51
  });
52
52
  }
53
- const bip44Params = adapter.buildBIP44Params({ accountNumber: Number(buyAssetAccountNumber) });
54
- const receiveAddress = await adapter.getAddress({ wallet, bip44Params });
55
53
  const slippagePercentage = slippage ? (0, bignumber_1.bnOrZero)(slippage).div(100).toString() : constants_1.DEFAULT_SLIPPAGE;
56
54
  const baseUrl = (0, helpers_2.baseUrlFromChainId)(buyAsset.chainId);
57
55
  const zrxService = (0, zrxService_1.zrxServiceFactory)(baseUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshiftoss/swapper",
3
- "version": "9.0.1",
3
+ "version": "9.2.0",
4
4
  "description": "An interface and implementations to trade coins via various protocols",
5
5
  "repository": "https://github.com/shapeshift/lib",
6
6
  "license": "MIT",