@shapeshiftoss/swapper 11.1.3 → 11.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.
@@ -0,0 +1,2 @@
1
+ import { ChainAdapterManager } from '@shapeshiftoss/chain-adapters';
2
+ export declare const getAdapterManager: () => ChainAdapterManager;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.getAdapterManager = void 0;
30
+ const chain_adapters_1 = require("@shapeshiftoss/chain-adapters");
31
+ const types_1 = require("@shapeshiftoss/types");
32
+ const unchained = __importStar(require("@shapeshiftoss/unchained-client"));
33
+ const dotenv_1 = __importDefault(require("dotenv"));
34
+ dotenv_1.default.config();
35
+ const { UNCHAINED_ETH_HTTPS_API = 'http://localhost:31300', UNCHAINED_ETH_WS_API = 'wss://localhost:31300', UNCHAINED_BTC_HTTPS_API = 'http://localhost:31300', UNCHAINED_BTC_WS_API = 'wss://localhost:31300', UNCHAINED_LTC_HTTPS_API = 'http://localhost:31300', UNCHAINED_LTC_WS_API = 'wss://localhost:31300', UNCHAINED_RUNE_HTTPS_API = 'http://localhost:31300', UNCHAINED_RUNE_WS_API = 'wss://localhost:31300', UNCHAINED_ATOM_HTTPS_API = 'http://localhost:31300', UNCHAINED_ATOM_WS_API = 'wss://localhost:31300', ETH_NODE_URL = 'http://localhost:31300', } = process.env;
36
+ const getAdapterManager = () => {
37
+ const ethChainAdapter = new chain_adapters_1.ethereum.ChainAdapter({
38
+ providers: {
39
+ ws: new unchained.ws.Client(UNCHAINED_ETH_WS_API),
40
+ http: new unchained.ethereum.V1Api(new unchained.ethereum.Configuration({
41
+ basePath: UNCHAINED_ETH_HTTPS_API,
42
+ })),
43
+ },
44
+ rpcUrl: ETH_NODE_URL,
45
+ });
46
+ const btcAdapterArgs = {
47
+ coinName: 'bitcoin',
48
+ providers: {
49
+ ws: new unchained.ws.Client(UNCHAINED_BTC_WS_API),
50
+ http: new unchained.bitcoin.V1Api(new unchained.bitcoin.Configuration({
51
+ basePath: UNCHAINED_BTC_HTTPS_API,
52
+ })),
53
+ },
54
+ };
55
+ const bitcoinChainAdapter = new chain_adapters_1.bitcoin.ChainAdapter(btcAdapterArgs);
56
+ const ltcAdapterArgs = {
57
+ coinName: 'litecoin',
58
+ providers: {
59
+ ws: new unchained.ws.Client(UNCHAINED_LTC_WS_API),
60
+ http: new unchained.bitcoin.V1Api(new unchained.bitcoin.Configuration({
61
+ basePath: UNCHAINED_LTC_HTTPS_API,
62
+ })),
63
+ },
64
+ };
65
+ const litecoinChainAdapter = new chain_adapters_1.bitcoin.ChainAdapter(ltcAdapterArgs);
66
+ const runeAdapterArgs = {
67
+ coinName: 'rune',
68
+ providers: {
69
+ ws: new unchained.ws.Client(UNCHAINED_RUNE_WS_API),
70
+ http: new unchained.thorchain.V1Api(new unchained.thorchain.Configuration({
71
+ basePath: UNCHAINED_RUNE_HTTPS_API,
72
+ })),
73
+ },
74
+ };
75
+ const thorchainChainAdapter = new chain_adapters_1.thorchain.ChainAdapter(runeAdapterArgs);
76
+ const cosmosAdapterArgs = {
77
+ coinName: 'atom',
78
+ providers: {
79
+ ws: new unchained.ws.Client(UNCHAINED_ATOM_WS_API),
80
+ http: new unchained.thorchain.V1Api(new unchained.thorchain.Configuration({
81
+ basePath: UNCHAINED_ATOM_HTTPS_API,
82
+ })),
83
+ },
84
+ };
85
+ const cosmosChainAdapter = new chain_adapters_1.cosmos.ChainAdapter(cosmosAdapterArgs);
86
+ const adapterManager = new Map();
87
+ adapterManager.set(types_1.KnownChainIds.BitcoinMainnet, bitcoinChainAdapter);
88
+ adapterManager.set(types_1.KnownChainIds.EthereumMainnet, ethChainAdapter);
89
+ adapterManager.set(types_1.KnownChainIds.ThorchainMainnet, thorchainChainAdapter);
90
+ adapterManager.set(types_1.KnownChainIds.LitecoinMainnet, litecoinChainAdapter);
91
+ adapterManager.set(types_1.KnownChainIds.CosmosMainnet, cosmosChainAdapter);
92
+ return adapterManager;
93
+ };
94
+ exports.getAdapterManager = getAdapterManager;
package/dist/api.d.ts CHANGED
@@ -74,6 +74,7 @@ export declare type SupportedSellAssetsInput = {
74
74
  assetIds: AssetId[];
75
75
  };
76
76
  declare type CommonTradeInput = {
77
+ bip44Params?: BIP44Params;
77
78
  sellAsset: Asset;
78
79
  buyAsset: Asset;
79
80
  sellAmount: string;
@@ -82,7 +83,7 @@ declare type CommonTradeInput = {
82
83
  receiveAddress: string;
83
84
  };
84
85
  export declare type EvmSupportedChainIds = KnownChainIds.EthereumMainnet | KnownChainIds.AvalancheMainnet;
85
- export declare type CosmosSdkSupportedChainIds = KnownChainIds.CosmosMainnet | KnownChainIds.OsmosisMainnet;
86
+ export declare type CosmosSdkSupportedChainIds = KnownChainIds.CosmosMainnet | KnownChainIds.OsmosisMainnet | KnownChainIds.ThorchainMainnet;
86
87
  export declare type EvmSupportedChainAdapters = ethereum.ChainAdapter | avalanche.ChainAdapter;
87
88
  export declare type CosmosSdkSupportedChainAdapters = cosmos.ChainAdapter | osmosis.ChainAdapter;
88
89
  export declare type UtxoSupportedChainIds = KnownChainIds.BitcoinMainnet | KnownChainIds.DogecoinMainnet | KnownChainIds.LitecoinMainnet | KnownChainIds.BitcoinCashMainnet;
@@ -1,44 +1,22 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  const asset_service_1 = require("@shapeshiftoss/asset-service");
7
+ const caip_1 = require("@shapeshiftoss/caip");
30
8
  const chain_adapters_1 = require("@shapeshiftoss/chain-adapters");
31
9
  const hdwallet_native_1 = require("@shapeshiftoss/hdwallet-native");
32
- const types_1 = require("@shapeshiftoss/types");
33
- const unchained = __importStar(require("@shapeshiftoss/unchained-client"));
34
10
  const bignumber_js_1 = __importDefault(require("bignumber.js"));
35
11
  const dotenv_1 = __importDefault(require("dotenv"));
36
12
  const readline_sync_1 = __importDefault(require("readline-sync"));
37
13
  const web3_1 = __importDefault(require("web3"));
14
+ const adapters_1 = require("./adapters");
38
15
  const SwapperManager_1 = require("./manager/SwapperManager");
16
+ const swappers_1 = require("./swappers");
39
17
  const ZrxSwapper_1 = require("./swappers/zrx/ZrxSwapper");
40
18
  dotenv_1.default.config();
41
- const { UNCHAINED_HTTP_API = 'http://localhost:31300', UNCHAINED_WS_API = 'wss://localhost:31300', ETH_NODE_URL = 'http://localhost:3000', DEVICE_ID = 'device123', MNEMONIC = 'all all all all all all all all all all all all', } = process.env;
19
+ const { ETH_NODE_URL = 'http://localhost:3000', MIDGARD_URL = 'https://dev-indexer.thorchain.shapeshift.com/v2', DEVICE_ID = 'device123', MNEMONIC = 'all all all all all all all all all all all all', } = process.env;
42
20
  const toBaseUnit = (amount, precision) => {
43
21
  return new bignumber_js_1.default(amount)
44
22
  .multipliedBy(new bignumber_js_1.default(10).exponentiatedBy(new bignumber_js_1.default(precision)))
@@ -62,7 +40,7 @@ const getWallet = async () => {
62
40
  const main = async () => {
63
41
  const [, , ...args] = process.argv;
64
42
  const [sellSymbol, buySymbol, sellAmount] = args;
65
- console.info(`sellSymbol: sell ${sellAmount} of ${sellSymbol} to ${buySymbol}`);
43
+ console.info(`sell ${sellAmount} of ${sellSymbol} to ${buySymbol}`);
66
44
  if (!sellAmount || !sellSymbol || !buySymbol) {
67
45
  console.error(`
68
46
  Usage:
@@ -82,64 +60,119 @@ const main = async () => {
82
60
  console.error(`No asset ${buySymbol} found in asset service`);
83
61
  return;
84
62
  }
63
+ const adapterManager = (0, adapters_1.getAdapterManager)();
64
+ const ethAdapter = adapterManager.get(caip_1.ethChainId);
65
+ if (!ethAdapter) {
66
+ throw new Error('no ethAdapter');
67
+ }
68
+ const sellAdapter = adapterManager.get(sellAsset.chainId);
69
+ if (!sellAdapter) {
70
+ throw new Error(`no sellAdapter ${sellAsset.chainId}`);
71
+ }
72
+ const buyAdapter = adapterManager.get(buyAsset.chainId);
73
+ if (!buyAdapter) {
74
+ throw new Error(`no buyAdapter ${buyAsset.chainId}`);
75
+ }
85
76
  // Swapper Deps
86
77
  const wallet = await getWallet();
87
- const ethChainAdapter = new chain_adapters_1.ethereum.ChainAdapter({
88
- providers: {
89
- ws: new unchained.ws.Client(UNCHAINED_WS_API),
90
- http: new unchained.ethereum.V1Api(new unchained.ethereum.Configuration({
91
- basePath: UNCHAINED_HTTP_API,
92
- })),
93
- },
94
- rpcUrl: 'https://mainnet.infura.io/v3/d734c7eebcdf400185d7eb67322a7e57',
95
- });
96
78
  const web3Provider = new web3_1.default.providers.HttpProvider(ETH_NODE_URL);
97
79
  const web3 = new web3_1.default(web3Provider);
98
- const zrxSwapperDeps = { wallet, adapter: ethChainAdapter, web3 };
99
- const manager = new SwapperManager_1.SwapperManager();
80
+ const zrxSwapperDeps = {
81
+ wallet,
82
+ adapter: ethAdapter,
83
+ web3,
84
+ };
85
+ const swapManager = new SwapperManager_1.SwapperManager();
100
86
  const zrxSwapper = new ZrxSwapper_1.ZrxSwapper(zrxSwapperDeps);
101
- manager.addSwapper(zrxSwapper);
102
- const swapper = await manager.getBestSwapper({
103
- sellAssetId: 'eip155:1/slip44:60',
104
- buyAssetId: 'eip155:1/erc20:0xc770eefad204b5180df6a14ee197d99d808ee52d',
87
+ swapManager.addSwapper(zrxSwapper);
88
+ const tcDeps = { midgardUrl: MIDGARD_URL, web3, adapterManager };
89
+ const tc = new swappers_1.ThorchainSwapper(tcDeps);
90
+ await tc.initialize();
91
+ swapManager.addSwapper(tc);
92
+ const swapper = await swapManager.getBestSwapper({
93
+ sellAssetId: sellAsset.assetId,
94
+ buyAssetId: buyAsset.assetId,
105
95
  });
106
- if (!swapper)
96
+ console.info(`using swapper ${swapper === null || swapper === void 0 ? void 0 : swapper.getType()}`);
97
+ if (!swapper) {
98
+ console.warn(`no swapper found for specified assets`);
107
99
  return;
100
+ }
101
+ if (sellAdapter == undefined || sellAdapter == null) {
102
+ throw new Error('huh');
103
+ }
104
+ let utxoAccountType;
105
+ if (sellAdapter.getSupportedAccountTypes) {
106
+ utxoAccountType = chain_adapters_1.bitcoin.ChainAdapter.defaultUtxoAccountType;
107
+ }
108
+ const bip44Params = sellAdapter.getBIP44Params({
109
+ accountNumber: 0,
110
+ accountType: utxoAccountType,
111
+ });
112
+ if (!bip44Params) {
113
+ throw new Error('falsy bip44Params');
114
+ }
115
+ const { chainNamespace: sellChainNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
116
+ switch (sellChainNamespace) {
117
+ case caip_1.CHAIN_NAMESPACE.Evm:
118
+ case caip_1.CHAIN_NAMESPACE.Utxo:
119
+ case caip_1.CHAIN_NAMESPACE.CosmosSdk:
120
+ }
121
+ let publicKey;
122
+ if (sellChainNamespace == caip_1.CHAIN_NAMESPACE.Utxo) {
123
+ if (!utxoAccountType) {
124
+ throw new Error('utxoAccountType must be defined');
125
+ }
126
+ publicKey = await sellAdapter.getPublicKey(wallet, bip44Params, utxoAccountType);
127
+ }
108
128
  const sellAmountBase = toBaseUnit(sellAmount, sellAsset.precision);
129
+ const buyAssetReceiveAddr = await buyAdapter.getAddress({
130
+ wallet,
131
+ accountType: utxoAccountType,
132
+ bip44Params,
133
+ });
134
+ console.info(`${buyAsset.name} using receive addr ${buyAssetReceiveAddr}`);
109
135
  let quote;
110
136
  try {
111
137
  quote = await swapper.getTradeQuote({
112
- chainId: types_1.KnownChainIds.EthereumMainnet,
138
+ chainId: sellAsset.chainId,
113
139
  sellAsset,
114
140
  buyAsset,
115
141
  sellAmount: sellAmountBase,
116
142
  sellAssetAccountNumber: 0,
117
143
  sendMax: false,
118
- receiveAddress: '0xc770eefad204b5180df6a14ee197d99d808ee52d',
144
+ accountType: utxoAccountType || chain_adapters_1.bitcoin.ChainAdapter.defaultUtxoAccountType,
145
+ bip44Params,
146
+ xpub: (publicKey === null || publicKey === void 0 ? void 0 : publicKey.xpub) || '',
147
+ receiveAddress: buyAssetReceiveAddr,
119
148
  });
120
149
  }
121
150
  catch (e) {
122
151
  console.error(e);
152
+ return;
123
153
  }
124
154
  if (!quote) {
155
+ console.warn('no quote returned');
125
156
  return;
126
157
  }
127
- console.info('quote = ', JSON.stringify(quote));
128
158
  const buyAmount = fromBaseUnit(quote.buyAmount || '0', buyAsset.precision);
129
159
  const answer = readline_sync_1.default.question(`Swap ${sellAmount} ${sellAsset.symbol} for ${buyAmount} ${buyAsset.symbol} on ${swapper.getType()}? (y/n): `);
130
160
  if (answer === 'y') {
131
161
  const trade = await swapper.buildTrade({
132
- chainId: types_1.KnownChainIds.EthereumMainnet,
162
+ chainId: sellAsset.chainId,
133
163
  wallet,
134
164
  buyAsset,
135
165
  sendMax: false,
136
166
  sellAmount: sellAmountBase,
137
167
  sellAsset,
138
168
  sellAssetAccountNumber: 0,
139
- receiveAddress: '0xc770eefad204b5180df6a14ee197d99d808ee52d',
169
+ receiveAddress: buyAssetReceiveAddr,
170
+ accountType: utxoAccountType || chain_adapters_1.bitcoin.ChainAdapter.defaultUtxoAccountType,
171
+ bip44Params,
172
+ xpub: (publicKey === null || publicKey === void 0 ? void 0 : publicKey.xpub) || '',
140
173
  });
141
- const txid = await swapper.executeTrade({ trade, wallet });
142
- console.info('broadcast tx with id: ', txid);
174
+ const tradeResult = await swapper.executeTrade({ trade, wallet });
175
+ console.info('broadcast tx with id: ', tradeResult.tradeId);
143
176
  }
144
177
  };
145
178
  main().then(() => console.info('Done'));
@@ -58,6 +58,7 @@ class ThorchainSwapper {
58
58
  acc.push(assetId);
59
59
  return acc;
60
60
  }, []);
61
+ this.supportedSellAssetIds.push(caip_1.thorchainAssetId);
61
62
  this.supportedBuyAssetIds = responseData.reduce((acc, midgardPool) => {
62
63
  const assetId = caip_1.adapters.poolAssetIdToAssetId(midgardPool.asset);
63
64
  if (!assetId || !this.buySupportedChainIds[(0, caip_1.fromAssetId)(assetId).chainId])
@@ -65,6 +66,7 @@ class ThorchainSwapper {
65
66
  acc.push(assetId);
66
67
  return acc;
67
68
  }, []);
69
+ this.supportedBuyAssetIds.push(caip_1.thorchainAssetId);
68
70
  }
69
71
  catch (e) {
70
72
  throw new api_1.SwapError('[thorchainInitialize]: initialize failed to set supportedAssetIds', {
@@ -163,8 +165,11 @@ class ThorchainSwapper {
163
165
  ? tradeResult.tradeId.slice(2)
164
166
  : tradeResult.tradeId;
165
167
  const { data: responseData } = await thorService_1.thorService.get(`${this.deps.midgardUrl}/actions?txid=${midgardTxid}`);
168
+ // https://gitlab.com/thorchain/thornode/-/blob/develop/common/tx.go#L22
169
+ // responseData?.actions[0].out[0].txID should be the txId for consistency, but the outbound Tx for Thor rune swaps is actually a BlankTxId
170
+ // so we use the buyTxId for completion detection
166
171
  const buyTxid = ((_a = responseData === null || responseData === void 0 ? void 0 : responseData.actions[0]) === null || _a === void 0 ? void 0 : _a.status) === 'success' && ((_b = responseData === null || responseData === void 0 ? void 0 : responseData.actions[0]) === null || _b === void 0 ? void 0 : _b.type) === 'swap'
167
- ? responseData === null || responseData === void 0 ? void 0 : responseData.actions[0].out[0].txID
172
+ ? midgardTxid
168
173
  : '';
169
174
  // This will detect all the errors I have seen.
170
175
  if (((_c = responseData === null || responseData === void 0 ? void 0 : responseData.actions[0]) === null || _c === void 0 ? void 0 : _c.status) === 'success' &&
@@ -12,7 +12,7 @@ const makeTradeTx_1 = require("../utils/ethereum/makeTradeTx");
12
12
  const buildTrade = async ({ deps, input, }) => {
13
13
  var _a, _b, _c, _d;
14
14
  try {
15
- const { buyAsset, sellAsset, sellAssetAccountNumber, sellAmount, wallet, slippage: slippageTolerance = constants_1.DEFAULT_SLIPPAGE, receiveAddress: destinationAddress, } = input;
15
+ const { bip44Params, buyAsset, receiveAddress: destinationAddress, sellAmount, sellAsset, sellAssetAccountNumber, slippage: slippageTolerance = constants_1.DEFAULT_SLIPPAGE, wallet, } = input;
16
16
  const quote = await (0, getTradeQuote_1.getThorTradeQuote)({ deps, input });
17
17
  const sellAdapter = deps.adapterManager.get(sellAsset.chainId);
18
18
  if (!sellAdapter)
@@ -78,7 +78,11 @@ const buildTrade = async ({ deps, input, }) => {
78
78
  };
79
79
  }
80
80
  else if (chainNamespace === caip_1.CHAIN_NAMESPACE.CosmosSdk) {
81
+ if (!bip44Params) {
82
+ throw new Error('bip44Params required as input');
83
+ }
81
84
  const txData = await (0, cosmosTxData_1.cosmosTxData)({
85
+ bip44Params,
82
86
  deps,
83
87
  sellAdapter: sellAdapter,
84
88
  sellAmount,
@@ -111,6 +115,7 @@ const buildTrade = async ({ deps, input, }) => {
111
115
  throw new api_1.SwapError('[buildTrade]: error building trade', {
112
116
  code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
113
117
  fn: 'buildTrade',
118
+ cause: e,
114
119
  });
115
120
  }
116
121
  };
@@ -17,7 +17,7 @@ const MINIMUM_USD_TRADE_AMOUNT = (0, bignumber_1.bn)(1);
17
17
  const getThorTradeQuote = async ({ deps, input }) => {
18
18
  const { sellAsset, buyAsset, sellAmount, sellAssetAccountNumber, chainId, receiveAddress } = input;
19
19
  try {
20
- const { assetReference: sellAssetErc20Address } = (0, caip_1.fromAssetId)(sellAsset.assetId);
20
+ const { assetReference: sellAssetReference } = (0, caip_1.fromAssetId)(sellAsset.assetId);
21
21
  const sellAdapter = deps.adapterManager.get(chainId);
22
22
  if (!sellAdapter)
23
23
  throw new api_1.SwapError(`[getThorTradeQuote] - No chain adapter found for ${chainId}.`, {
@@ -62,7 +62,7 @@ const getThorTradeQuote = async ({ deps, input }) => {
62
62
  });
63
63
  const feeData = await (0, getEthTxFees_1.getEthTxFees)({
64
64
  adapterManager: deps.adapterManager,
65
- sellAssetReference: sellAssetErc20Address,
65
+ sellAssetReference,
66
66
  tradeFee,
67
67
  });
68
68
  return {
@@ -3,9 +3,11 @@ import { ChainId } from '@shapeshiftoss/caip';
3
3
  import { ChainAdapter } from '@shapeshiftoss/chain-adapters';
4
4
  import { HDWallet } from '@shapeshiftoss/hdwallet-core';
5
5
  import { KnownChainIds } from '@shapeshiftoss/types';
6
+ import { BIP44Params } from '@shapeshiftoss/types';
6
7
  import { TradeQuote } from '../../../../api';
7
8
  import { ThorchainSwapperDeps } from '../../types';
8
9
  export declare const cosmosTxData: (input: {
10
+ bip44Params: BIP44Params;
9
11
  destinationAddress: string;
10
12
  deps: ThorchainSwapperDeps;
11
13
  sellAmount: string;
@@ -1,16 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cosmosTxData = void 0;
4
+ const types_1 = require("@shapeshiftoss/types");
4
5
  const api_1 = require("../../../../api");
5
6
  const getLimit_1 = require("../getLimit/getLimit");
6
7
  const makeSwapMemo_1 = require("../makeSwapMemo/makeSwapMemo");
7
8
  const thorService_1 = require("../thorService");
8
9
  const cosmosTxData = async (input) => {
9
- const { deps, destinationAddress, sellAmount, sellAsset, buyAsset, slippageTolerance, quote, wallet, sellAdapter, } = input;
10
+ const { bip44Params, deps, destinationAddress, sellAmount, sellAsset, buyAsset, slippageTolerance, quote, wallet, sellAdapter, } = input;
11
+ const fromThorAsset = sellAsset.chainId == types_1.KnownChainIds.ThorchainMainnet;
10
12
  const { data: inboundAddresses } = await thorService_1.thorService.get(`${deps.midgardUrl}/thorchain/inbound_addresses`);
11
13
  const atomInboundAddresses = inboundAddresses.find((inbound) => inbound.chain === 'GAIA');
12
14
  const vault = atomInboundAddresses === null || atomInboundAddresses === void 0 ? void 0 : atomInboundAddresses.address;
13
- if (!vault)
15
+ if (!vault && !fromThorAsset)
14
16
  throw new api_1.SwapError('[buildTrade]: no vault for chain', {
15
17
  code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
16
18
  fn: 'buildTrade',
@@ -31,7 +33,25 @@ const cosmosTxData = async (input) => {
31
33
  destinationAddress,
32
34
  limit,
33
35
  });
36
+ if (fromThorAsset) {
37
+ const buildTxResponse = await sellAdapter.buildDepositTransaction({
38
+ bip44Params,
39
+ value: sellAmount,
40
+ wallet,
41
+ memo,
42
+ gas: quote.feeData.chainSpecific.estimatedGas,
43
+ fee: quote.feeData.fee,
44
+ });
45
+ return buildTxResponse.txToSign;
46
+ }
47
+ if (!vault)
48
+ throw new api_1.SwapError('[buildTrade]: no vault for chain', {
49
+ code: api_1.SwapErrorTypes.BUILD_TRADE_FAILED,
50
+ fn: 'buildTrade',
51
+ details: { chainId: input.chainId },
52
+ });
34
53
  const buildTxResponse = await sellAdapter.buildSendTransaction({
54
+ bip44Params,
35
55
  value: sellAmount,
36
56
  wallet,
37
57
  to: vault,
@@ -5,8 +5,13 @@ const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../api");
6
6
  const constants_1 = require("../constants");
7
7
  const getPriceRatio_1 = require("../getPriceRatio/getPriceRatio");
8
+ const isRune_1 = require("../isRune/isRune");
8
9
  const thorService_1 = require("../thorService");
10
+ // TODO rename this estimateOutboundFee or similar
9
11
  const estimateTradeFee = async (deps, buyAsset) => {
12
+ if ((0, isRune_1.isRune)(buyAsset.assetId)) {
13
+ return '0.02'; // todo - read from TC mimir/constants
14
+ }
10
15
  const thorId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAsset.assetId });
11
16
  if (!thorId)
12
17
  throw new api_1.SwapError('[estimateTradeFee] - undefined thorId for given buyAssetId', {
@@ -4,8 +4,8 @@ exports.getTradeRate = 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 bignumber_2 = require("../../../utils/bignumber");
8
7
  const getPriceRatio_1 = require("../getPriceRatio/getPriceRatio");
8
+ const isRune_1 = require("../isRune/isRune");
9
9
  const thorService_1 = require("../thorService");
10
10
  const THOR_PRECISION = 8;
11
11
  const getSwapOutput = (inputAmount, poolData, toRune) => {
@@ -33,33 +33,74 @@ const getTradeRate = async (sellAsset, buyAssetId, sellAmount, deps) => {
33
33
  }
34
34
  const buyPoolId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAssetId });
35
35
  const sellPoolId = caip_1.adapters.assetIdToPoolAssetId({ assetId: sellAsset.assetId });
36
- if (!buyPoolId || !sellPoolId)
37
- throw new api_1.SwapError(`[getPriceRatio]: No thorchain pool found`, {
36
+ if (!buyPoolId && !(0, isRune_1.isRune)(buyAssetId)) {
37
+ throw new api_1.SwapError(`[getTradeRate]: No buyPoolId for asset ${buyAssetId}`, {
38
38
  code: api_1.SwapErrorTypes.POOL_NOT_FOUND,
39
- fn: 'getPriceRatio',
40
- details: { buyPoolId, sellPoolId },
39
+ fn: 'getTradeRate',
40
+ details: { buyAssetId },
41
41
  });
42
+ }
43
+ if (!sellPoolId && !(0, isRune_1.isRune)(sellAsset.assetId)) {
44
+ throw new api_1.SwapError(`[getTradeRate]: No sellPoolId for asset ${sellAsset.assetId}`, {
45
+ code: api_1.SwapErrorTypes.POOL_NOT_FOUND,
46
+ fn: 'getTradeRate',
47
+ details: { sellAsset: sellAsset.assetId },
48
+ });
49
+ }
42
50
  const { data: responseData } = await thorService_1.thorService.get(`${deps.midgardUrl}/pools`);
43
- const buyPool = responseData.find((response) => response.asset === buyPoolId);
44
- const sellPool = responseData.find((response) => response.asset === sellPoolId);
45
- if (!buyPool || !sellPool)
46
- throw new api_1.SwapError(`[getPriceRatio]: no pools found for`, {
51
+ const buyPool = buyPoolId && responseData.find((response) => response.asset === buyPoolId);
52
+ const sellPool = sellPoolId && responseData.find((response) => response.asset === sellPoolId);
53
+ if (buyPoolId && !buyPool) {
54
+ throw new api_1.SwapError(`[getTradeRate]: No buyPool for poolId ${buyPoolId}`, {
47
55
  code: api_1.SwapErrorTypes.POOL_NOT_FOUND,
48
- fn: 'getPriceRatio',
49
- details: { buyPoolId, sellPoolId },
56
+ fn: 'getTradeRate',
57
+ details: { buyPoolId },
50
58
  });
59
+ }
60
+ if (sellPoolId && !sellPool) {
61
+ throw new api_1.SwapError(`[getTradeRate]: No sellPool for poolId ${sellPoolId}`, {
62
+ code: api_1.SwapErrorTypes.POOL_NOT_FOUND,
63
+ fn: 'getTradeRate',
64
+ details: { sellPoolId },
65
+ });
66
+ }
51
67
  // All thorchain pool amounts are base 8 regardless of token precision
52
- const sellBaseAmount = (0, bignumber_1.bn)((0, bignumber_2.toBaseUnit)((0, bignumber_2.fromBaseUnit)(sellAmount, sellAsset.precision), THOR_PRECISION));
53
- const sellAssetPoolData = {
54
- assetBalance: (0, bignumber_1.bn)(sellPool.assetDepth),
55
- runeBalance: (0, bignumber_1.bn)(sellPool.runeDepth),
56
- };
57
- const buyAssetPoolData = {
58
- assetBalance: (0, bignumber_1.bn)(buyPool.assetDepth),
59
- runeBalance: (0, bignumber_1.bn)(buyPool.runeDepth),
60
- };
61
- const outputAmountBase8 = getDoubleSwapOutput(sellBaseAmount, sellAssetPoolData, buyAssetPoolData);
62
- const outputAmount = (0, bignumber_2.fromBaseUnit)(outputAmountBase8, THOR_PRECISION);
63
- return (0, bignumber_1.bn)(outputAmount).div((0, bignumber_2.fromBaseUnit)(sellAmount, sellAsset.precision)).toString();
68
+ const sellBaseAmount = (0, bignumber_1.bn)((0, bignumber_1.toBaseUnit)((0, bignumber_1.fromBaseUnit)(sellAmount, sellAsset.precision), THOR_PRECISION));
69
+ const doubleSwap = buyPool != undefined && sellPool != undefined;
70
+ const toRune = buyAssetId === caip_1.thorchainAssetId;
71
+ const outputAmountBase8 = (() => {
72
+ if (doubleSwap && sellPool && buyPool) {
73
+ const sellAssetPoolData = {
74
+ assetBalance: (0, bignumber_1.bn)(sellPool.assetDepth),
75
+ runeBalance: (0, bignumber_1.bn)(sellPool.runeDepth),
76
+ };
77
+ const buyAssetPoolData = {
78
+ assetBalance: (0, bignumber_1.bn)(buyPool.assetDepth),
79
+ runeBalance: (0, bignumber_1.bn)(buyPool.runeDepth),
80
+ };
81
+ return getDoubleSwapOutput(sellBaseAmount, sellAssetPoolData, buyAssetPoolData);
82
+ }
83
+ else {
84
+ const poolData = sellPool
85
+ ? {
86
+ assetBalance: (0, bignumber_1.bn)(sellPool.assetDepth),
87
+ runeBalance: (0, bignumber_1.bn)(sellPool.runeDepth),
88
+ }
89
+ : buyPool && {
90
+ assetBalance: (0, bignumber_1.bn)(buyPool.assetDepth),
91
+ runeBalance: (0, bignumber_1.bn)(buyPool.runeDepth),
92
+ };
93
+ if (!poolData) {
94
+ throw new api_1.SwapError(`[getPriceRatio]: No pool data for rune swap`, {
95
+ code: api_1.SwapErrorTypes.POOL_NOT_FOUND,
96
+ fn: 'getPriceRatio',
97
+ details: { buyAssetId, sellAsset, buyPool, sellPool },
98
+ });
99
+ }
100
+ return getSwapOutput(sellBaseAmount, poolData, toRune);
101
+ }
102
+ })();
103
+ const outputAmount = (0, bignumber_1.fromBaseUnit)(outputAmountBase8, THOR_PRECISION);
104
+ return (0, bignumber_1.bn)(outputAmount).div((0, bignumber_1.fromBaseUnit)(sellAmount, sellAsset.precision)).toString();
64
105
  };
65
106
  exports.getTradeRate = getTradeRate;
@@ -3,19 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getUsdRate = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
5
  const api_1 = require("../../../../api");
6
+ const bignumber_1 = require("../../../utils/bignumber");
7
+ const isRune_1 = require("../isRune/isRune");
6
8
  const thorService_1 = require("../thorService");
9
+ const THOR_PRECISION = 8;
10
+ // not sure what to do for rune usd rate - inverting USDC pool rate for now
11
+ const usdcPool = 'ETH.USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48';
7
12
  const getUsdRate = async ({ deps, input, }) => {
8
13
  const { assetId } = input;
9
14
  try {
10
- const thorchainPoolId = caip_1.adapters.assetIdToPoolAssetId({ assetId });
15
+ const thorchainPoolId = (() => {
16
+ if ((0, isRune_1.isRune)(assetId)) {
17
+ return usdcPool;
18
+ }
19
+ return caip_1.adapters.assetIdToPoolAssetId({ assetId });
20
+ })();
11
21
  if (!thorchainPoolId)
12
22
  throw new api_1.SwapError(`[getUsdRate]: No thorchainPoolId found for assetId: ${assetId}`, {
13
23
  code: api_1.SwapErrorTypes.USD_RATE_FAILED,
14
24
  });
15
25
  const { data: responseData } = await thorService_1.thorService.get(`${deps.midgardUrl}/pool/${thorchainPoolId}`);
16
- const rate = responseData === null || responseData === void 0 ? void 0 : responseData.assetPriceUSD;
26
+ const rate = (() => {
27
+ if ((0, isRune_1.isRune)(assetId)) {
28
+ const bnRate = (0, bignumber_1.bnOrZero)(responseData === null || responseData === void 0 ? void 0 : responseData.assetPrice);
29
+ if (bnRate.isZero()) {
30
+ throw new api_1.SwapError('[getUsdRate]: cannot invert rate zero', {
31
+ code: api_1.SwapErrorTypes.USD_RATE_FAILED,
32
+ });
33
+ }
34
+ const inverseRate = (0, bignumber_1.bn)(1).div(bnRate);
35
+ return inverseRate.toFixed(THOR_PRECISION);
36
+ }
37
+ return responseData === null || responseData === void 0 ? void 0 : responseData.assetPriceUSD;
38
+ })();
17
39
  if (!rate)
18
- throw new api_1.SwapError(`[getUsdRate]: No rate found`, {
40
+ throw new api_1.SwapError(`[getUsdRate]: No rate for ${assetId}`, {
19
41
  code: api_1.SwapErrorTypes.USD_RATE_FAILED,
20
42
  });
21
43
  return rate;
@@ -0,0 +1,2 @@
1
+ import { AssetId } from '@shapeshiftoss/caip';
2
+ export declare const isRune: (assetId: AssetId) => boolean;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isRune = void 0;
4
+ const caip_1 = require("@shapeshiftoss/caip");
5
+ const isRune = (assetId) => assetId === caip_1.thorchainAssetId;
6
+ exports.isRune = isRune;
@@ -6,12 +6,15 @@ const api_1 = require("../../../../api");
6
6
  const constants_1 = require("../constants");
7
7
  // BTC (and likely other utxo coins) can only support up to 80 character memos
8
8
  const MAX_LENGTH = 80;
9
+ // use symbol of thor Asset?
10
+ const runeThorId = 'RUNE';
9
11
  /**
10
12
  * @returns thorchain memo shortened to a max of 80 characters as described:
11
13
  * https://dev.thorchain.org/thorchain-dev/memos#mechanism-for-transaction-intent
12
14
  */
13
15
  const makeSwapMemo = ({ buyAssetId, destinationAddress, limit, }) => {
14
- const thorId = caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAssetId });
16
+ const isRune = buyAssetId === caip_1.thorchainAssetId;
17
+ const thorId = isRune ? runeThorId : caip_1.adapters.assetIdToPoolAssetId({ assetId: buyAssetId });
15
18
  if (!thorId)
16
19
  throw new api_1.SwapError('[makeSwapMemo] - undefined thorId for given buyAssetId', {
17
20
  code: api_1.SwapErrorTypes.MAKE_MEMO_FAILED,
@@ -10,6 +10,7 @@ export declare const setupQuote: () => {
10
10
  };
11
11
  export declare const setupBuildTrade: () => {
12
12
  buildTradeInput: {
13
+ bip44Params?: import("@shapeshiftoss/types").BIP44Params | undefined;
13
14
  sellAsset: Asset;
14
15
  buyAsset: Asset;
15
16
  sellAmount: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshiftoss/swapper",
3
- "version": "11.1.3",
3
+ "version": "11.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",