@shapeshiftoss/swapper 9.8.0 → 9.10.1

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/api.d.ts CHANGED
@@ -42,6 +42,18 @@ declare type ChainSpecificQuoteFeeData<T extends ChainId> = ChainSpecific<T, {
42
42
  byteCount: string;
43
43
  satsPerByte: string;
44
44
  };
45
+ [KnownChainIds.DogecoinMainnet]: {
46
+ byteCount: string;
47
+ satsPerByte: string;
48
+ };
49
+ [KnownChainIds.LitecoinMainnet]: {
50
+ byteCount: string;
51
+ satsPerByte: string;
52
+ };
53
+ [KnownChainIds.BitcoinCashMainnet]: {
54
+ byteCount: string;
55
+ satsPerByte: string;
56
+ };
45
57
  [KnownChainIds.CosmosMainnet]: {
46
58
  estimatedGas: string;
47
59
  };
@@ -74,19 +86,20 @@ export declare type EvmSupportedChainIds = KnownChainIds.EthereumMainnet | Known
74
86
  export declare type CosmosSdkSupportedChainIds = KnownChainIds.CosmosMainnet | KnownChainIds.OsmosisMainnet;
75
87
  export declare type EvmSupportedChainAdapters = ethereum.ChainAdapter | avalanche.ChainAdapter;
76
88
  export declare type CosmosSdkSupportedChainAdapters = cosmos.ChainAdapter | osmosis.ChainAdapter;
89
+ export declare type UtxoSupportedChainIds = KnownChainIds.BitcoinMainnet | KnownChainIds.DogecoinMainnet | KnownChainIds.LitecoinMainnet | KnownChainIds.BitcoinCashMainnet;
77
90
  export declare type GetEvmTradeQuoteInput = CommonTradeInput & {
78
91
  chainId: EvmSupportedChainIds;
79
92
  };
80
93
  export declare type GetCosmosSdkTradeQuoteInput = CommonTradeInput & {
81
94
  chainId: CosmosSdkSupportedChainIds;
82
95
  };
83
- declare type GetBtcTradeQuoteInput = CommonTradeInput & {
84
- chainId: KnownChainIds.BitcoinMainnet;
96
+ export declare type GetUtxoTradeQuoteInput = CommonTradeInput & {
97
+ chainId: UtxoSupportedChainIds;
85
98
  accountType: UtxoAccountType;
86
99
  bip44Params: BIP44Params;
87
100
  wallet: HDWallet;
88
101
  };
89
- export declare type GetTradeQuoteInput = GetBtcTradeQuoteInput | GetEvmTradeQuoteInput | GetCosmosSdkTradeQuoteInput;
102
+ export declare type GetTradeQuoteInput = GetUtxoTradeQuoteInput | GetEvmTradeQuoteInput | GetCosmosSdkTradeQuoteInput;
90
103
  export declare type BuildTradeInput = GetTradeQuoteInput & {
91
104
  slippage?: string;
92
105
  wallet: HDWallet;
@@ -65,20 +65,12 @@ async function cowBuildTrade(deps, input) {
65
65
  spenderAddress: constants_1.COW_SWAP_VAULT_RELAYER_ADDRESS,
66
66
  contractAddress: sellAssetErc20Address
67
67
  });
68
- const [feeDataOptions, sellAssetUsdRate] = await Promise.all([
69
- adapter.getFeeData({
70
- to: sellAssetErc20Address,
71
- value: '0',
72
- chainSpecific: { from: receiveAddress, contractData: data }
73
- }),
74
- (0, helpers_2.getUsdRate)(deps, sellAsset)
75
- ]);
68
+ const feeDataOptions = await adapter.getFeeData({
69
+ to: sellAssetErc20Address,
70
+ value: '0',
71
+ chainSpecific: { from: receiveAddress, contractData: data }
72
+ });
76
73
  const feeData = feeDataOptions['fast'];
77
- // calculating trade fee in USD
78
- const tradeFeeFiat = (0, bignumber_1.bnOrZero)(quote.feeAmount)
79
- .div((0, bignumber_1.bn)(10).exponentiatedBy(sellAsset.precision))
80
- .multipliedBy((0, bignumber_1.bnOrZero)(sellAssetUsdRate))
81
- .toString();
82
74
  const trade = {
83
75
  rate,
84
76
  feeData: {
@@ -87,7 +79,7 @@ async function cowBuildTrade(deps, input) {
87
79
  estimatedGas: feeData.chainSpecific.gasLimit,
88
80
  gasPrice: feeData.chainSpecific.gasPrice
89
81
  },
90
- tradeFee: tradeFeeFiat
82
+ tradeFee: '0' // Trade fees for buy Asset are always 0 since trade fees are subtracted from sell asset
91
83
  },
92
84
  sellAmount: normalizedSellAmount,
93
85
  buyAmount: quote.buyAmount,
@@ -6,7 +6,7 @@ export declare const DEFAULT_SOURCE: {
6
6
  proportion: string;
7
7
  }[];
8
8
  export declare const DEFAULT_ADDRESS = "0x0000000000000000000000000000000000000000";
9
- export declare const DEFAULT_APP_DATA = "0x0000000000000000000000000000000000000000000000000000000000000000";
9
+ export declare const DEFAULT_APP_DATA = "0x68a7b5781dfe48bd5d7aeb11261c17517f5c587da682e4fade9b6a00a59b8970";
10
10
  export declare const COW_SWAP_VAULT_RELAYER_ADDRESS = "0xc92e8bdf79f0507f65a392b0ab4667716bfe0110";
11
11
  export declare const COW_SWAP_SETTLEMENT_ADDRESS = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";
12
12
  export declare const WETH_ASSET_ID = "eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
@@ -7,7 +7,7 @@ exports.MAX_COWSWAP_TRADE = '100000000000000000000000000';
7
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
- exports.DEFAULT_APP_DATA = constants_1.HashZero;
10
+ exports.DEFAULT_APP_DATA = '0x68a7b5781dfe48bd5d7aeb11261c17517f5c587da682e4fade9b6a00a59b8970';
11
11
  exports.COW_SWAP_VAULT_RELAYER_ADDRESS = '0xc92e8bdf79f0507f65a392b0ab4667716bfe0110';
12
12
  exports.COW_SWAP_SETTLEMENT_ADDRESS = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41';
13
13
  exports.WETH_ASSET_ID = 'eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
@@ -3,6 +3,7 @@ import { AssetId, ChainId } from '@shapeshiftoss/caip';
3
3
  import { KnownChainIds } from '@shapeshiftoss/types';
4
4
  import { ApprovalNeededInput, ApprovalNeededOutput, ApproveInfiniteInput, BuildTradeInput, BuyAssetBySellIdInput, ExecuteTradeInput, GetTradeQuoteInput, Swapper, SwapperType, Trade, TradeQuote, TradeResult, TradeTxs } from '../../api';
5
5
  import { ThorchainSwapperDeps } from './types';
6
+ export * from './types';
6
7
  export declare class ThorchainSwapper implements Swapper<ChainId> {
7
8
  readonly name = "Thorchain";
8
9
  private sellSupportedChainIds;
@@ -1,4 +1,18 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.ThorchainSwapper = void 0;
4
18
  const caip_1 = require("@shapeshiftoss/caip");
@@ -10,12 +24,16 @@ const thorTradeApprovalNeeded_1 = require("./thorTradeApprovalNeeded/thorTradeAp
10
24
  const thorTradeApproveInfinite_1 = require("./thorTradeApproveInfinite/thorTradeApproveInfinite");
11
25
  const getUsdRate_1 = require("./utils/getUsdRate/getUsdRate");
12
26
  const thorService_1 = require("./utils/thorService");
27
+ __exportStar(require("./types"), exports);
13
28
  class ThorchainSwapper {
14
29
  constructor(deps) {
15
30
  this.name = 'Thorchain';
16
31
  this.sellSupportedChainIds = {
17
32
  [types_1.KnownChainIds.EthereumMainnet]: true,
18
33
  [types_1.KnownChainIds.BitcoinMainnet]: true,
34
+ [types_1.KnownChainIds.DogecoinMainnet]: true,
35
+ [types_1.KnownChainIds.LitecoinMainnet]: true,
36
+ [types_1.KnownChainIds.BitcoinCashMainnet]: true,
19
37
  [types_1.KnownChainIds.CosmosMainnet]: true
20
38
  };
21
39
  this.buySupportedChainIds = {
@@ -92,7 +110,8 @@ class ThorchainSwapper {
92
110
  details: { chainId: trade.sellAsset.chainId },
93
111
  fn: 'executeTrade'
94
112
  });
95
- if (trade.sellAsset.chainId === types_1.KnownChainIds.EthereumMainnet) {
113
+ const { chainNamespace } = (0, caip_1.fromAssetId)(trade.sellAsset.assetId);
114
+ if (chainNamespace === caip_1.CHAIN_NAMESPACE.Ethereum) {
96
115
  const signedTx = await adapter.signTransaction({
97
116
  txToSign: trade.txData,
98
117
  wallet
@@ -100,7 +119,7 @@ class ThorchainSwapper {
100
119
  const txid = await adapter.broadcastTransaction(signedTx);
101
120
  return { tradeId: txid };
102
121
  }
103
- else if (trade.sellAsset.chainId === types_1.KnownChainIds.BitcoinMainnet) {
122
+ else if (chainNamespace === caip_1.CHAIN_NAMESPACE.Bitcoin) {
104
123
  const signedTx = await adapter.signTransaction({
105
124
  txToSign: trade.txData,
106
125
  wallet
@@ -108,7 +127,7 @@ class ThorchainSwapper {
108
127
  const txid = await adapter.broadcastTransaction(signedTx);
109
128
  return { tradeId: txid };
110
129
  }
111
- else if (trade.sellAsset.chainId === types_1.KnownChainIds.CosmosMainnet) {
130
+ else if (chainNamespace === caip_1.CHAIN_NAMESPACE.Cosmos) {
112
131
  const signedTx = await adapter.signTransaction({
113
132
  txToSign: trade.txData,
114
133
  wallet
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildTrade = void 0;
4
+ const caip_1 = require("@shapeshiftoss/caip");
4
5
  const types_1 = require("@shapeshiftoss/types");
5
6
  const api_1 = require("../../../api");
6
7
  const constants_1 = require("../../utils/constants");
@@ -20,10 +21,11 @@ const buildTrade = async ({ deps, input }) => {
20
21
  fn: 'buildTrade',
21
22
  details: { sellAsset }
22
23
  });
23
- const sellAssetBip44Params = sellAdapter.buildBIP44Params({
24
- accountNumber: sellAssetAccountNumber
25
- });
26
- if (input.chainId === types_1.KnownChainIds.EthereumMainnet) {
24
+ const { chainNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
25
+ if (chainNamespace === caip_1.CHAIN_NAMESPACE.Ethereum) {
26
+ const sellAssetBip44Params = sellAdapter.buildBIP44Params({
27
+ accountNumber: sellAssetAccountNumber
28
+ });
27
29
  const ethTradeTx = await (0, makeTradeTx_1.makeTradeTx)({
28
30
  wallet,
29
31
  slippageTolerance,
@@ -45,7 +47,7 @@ const buildTrade = async ({ deps, input }) => {
45
47
  txData: ethTradeTx.txToSign
46
48
  };
47
49
  }
48
- else if (input.chainId === types_1.KnownChainIds.BitcoinMainnet) {
50
+ else if (chainNamespace === caip_1.CHAIN_NAMESPACE.Bitcoin) {
49
51
  const { vault, opReturnData } = await (0, getThorTxData_1.getThorTxInfo)({
50
52
  deps,
51
53
  sellAsset,
@@ -54,7 +56,7 @@ const buildTrade = async ({ deps, input }) => {
54
56
  slippageTolerance,
55
57
  destinationAddress,
56
58
  wallet,
57
- bip44Params: sellAssetBip44Params,
59
+ bip44Params: input.bip44Params,
58
60
  accountType: input.accountType,
59
61
  tradeFee: quote.feeData.tradeFee
60
62
  });
@@ -62,6 +64,7 @@ const buildTrade = async ({ deps, input }) => {
62
64
  value: sellAmount,
63
65
  wallet,
64
66
  to: vault,
67
+ bip44Params: input.bip44Params,
65
68
  chainSpecific: {
66
69
  accountType: input.accountType,
67
70
  satoshiPerByte: quote.feeData.chainSpecific
@@ -70,13 +73,13 @@ const buildTrade = async ({ deps, input }) => {
70
73
  }
71
74
  });
72
75
  return {
73
- chainId: types_1.KnownChainIds.BitcoinMainnet,
76
+ chainId: sellAsset.chainId,
74
77
  ...quote,
75
78
  receiveAddress: destinationAddress,
76
79
  txData: buildTxResponse.txToSign
77
80
  };
78
81
  }
79
- else if (input.chainId === types_1.KnownChainIds.CosmosMainnet) {
82
+ else if (chainNamespace === caip_1.CHAIN_NAMESPACE.Cosmos) {
80
83
  const txData = await (0, cosmosTxData_1.cosmosTxData)({
81
84
  deps,
82
85
  sellAdapter: sellAdapter,
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getThorTradeQuote = void 0;
4
4
  const caip_1 = require("@shapeshiftoss/caip");
5
- const types_1 = require("@shapeshiftoss/types");
6
5
  const api_1 = require("../../../api");
7
6
  const bignumber_1 = require("../../utils/bignumber");
8
7
  const constants_1 = require("../../utils/constants");
@@ -46,8 +45,9 @@ const getThorTradeQuote = async ({ deps, input }) => {
46
45
  sellAssetAccountNumber,
47
46
  minimum
48
47
  };
49
- switch (chainId) {
50
- case types_1.KnownChainIds.EthereumMainnet:
48
+ const { chainNamespace } = (0, caip_1.fromAssetId)(sellAsset.assetId);
49
+ switch (chainNamespace) {
50
+ case caip_1.CHAIN_NAMESPACE.Ethereum:
51
51
  return (async () => {
52
52
  const { router } = await (0, getThorTxData_2.getThorTxInfo)({
53
53
  deps,
@@ -69,7 +69,7 @@ const getThorTradeQuote = async ({ deps, input }) => {
69
69
  feeData
70
70
  };
71
71
  })();
72
- case types_1.KnownChainIds.BitcoinMainnet:
72
+ case caip_1.CHAIN_NAMESPACE.Bitcoin:
73
73
  return (async () => {
74
74
  const { vault, opReturnData, pubkey } = await (0, getThorTxData_1.getThorTxInfo)({
75
75
  deps,
@@ -88,7 +88,7 @@ const getThorTradeQuote = async ({ deps, input }) => {
88
88
  vault,
89
89
  opReturnData,
90
90
  pubkey,
91
- adapterManager: deps.adapterManager,
91
+ sellAdapter: sellAdapter,
92
92
  tradeFee
93
93
  });
94
94
  return {
@@ -97,7 +97,7 @@ const getThorTradeQuote = async ({ deps, input }) => {
97
97
  feeData
98
98
  };
99
99
  })();
100
- case types_1.KnownChainIds.CosmosMainnet:
100
+ case caip_1.CHAIN_NAMESPACE.Cosmos:
101
101
  return (async () => {
102
102
  const feeData = await sellAdapter.getFeeData({});
103
103
  return {
@@ -3,7 +3,7 @@ import { ChainAdapterManager } from '@shapeshiftoss/chain-adapters';
3
3
  import { BTCSignTx, CosmosSignTx, ETHSignTx } from '@shapeshiftoss/hdwallet-core';
4
4
  import { KnownChainIds } from '@shapeshiftoss/types';
5
5
  import type Web3 from 'web3';
6
- import { Trade } from '../../api';
6
+ import { Trade, UtxoSupportedChainIds } from '../../api';
7
7
  export declare type PoolResponse = {
8
8
  asset: string;
9
9
  assetDepth: string;
@@ -49,7 +49,7 @@ export declare type ThorchainSwapperDeps = {
49
49
  web3: Web3;
50
50
  };
51
51
  export interface BtcThorTrade<C extends ChainId> extends Trade<C> {
52
- chainId: KnownChainIds.BitcoinMainnet;
52
+ chainId: UtxoSupportedChainIds;
53
53
  txData: BTCSignTx;
54
54
  }
55
55
  export interface EthThorTrade<C extends ChainId> extends Trade<C> {
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getThorTxInfo = void 0;
4
- const caip_1 = require("@shapeshiftoss/caip");
5
4
  const api_1 = require("../../../../../api");
6
5
  const getLimit_1 = require("../../getLimit/getLimit");
7
6
  const makeSwapMemo_1 = require("../../makeSwapMemo/makeSwapMemo");
@@ -9,12 +8,12 @@ const thorService_1 = require("../../thorService");
9
8
  const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTolerance, destinationAddress, wallet, bip44Params, accountType, tradeFee }) => {
10
9
  try {
11
10
  const { data: inboundAddresses } = await thorService_1.thorService.get(`${deps.midgardUrl}/thorchain/inbound_addresses`);
12
- const btcInboundAddresses = inboundAddresses.find((inbound) => inbound.chain === 'BTC');
13
- const vault = btcInboundAddresses === null || btcInboundAddresses === void 0 ? void 0 : btcInboundAddresses.address;
11
+ const sellAssetInboundAddresses = inboundAddresses.find((inbound) => inbound.chain === sellAsset.symbol.toUpperCase());
12
+ const vault = sellAssetInboundAddresses === null || sellAssetInboundAddresses === void 0 ? void 0 : sellAssetInboundAddresses.address;
14
13
  if (!vault)
15
- throw new api_1.SwapError(`[getThorTxInfo]: vault not found for BTC`, {
14
+ throw new api_1.SwapError(`[getThorTxInfo]: vault not found for asset`, {
16
15
  code: api_1.SwapErrorTypes.RESPONSE_ERROR,
17
- details: { inboundAddresses }
16
+ details: { inboundAddresses, sellAsset }
18
17
  });
19
18
  const limit = await (0, getLimit_1.getLimit)({
20
19
  buyAssetId: buyAsset.assetId,
@@ -31,7 +30,7 @@ const getThorTxInfo = async ({ deps, sellAsset, buyAsset, sellAmount, slippageTo
31
30
  destinationAddress,
32
31
  limit
33
32
  });
34
- const adapter = deps.adapterManager.get(caip_1.btcChainId);
33
+ const adapter = deps.adapterManager.get(sellAsset.chainId);
35
34
  const pubkey = await adapter.getPublicKey(wallet, bip44Params, accountType);
36
35
  return {
37
36
  opReturnData: memo,
@@ -1,11 +1,10 @@
1
- import { ChainAdapterManager } from '@shapeshiftoss/chain-adapters';
2
- import { KnownChainIds } from '@shapeshiftoss/types';
3
- import { QuoteFeeData } from '../../../../../api';
4
- export declare const getBtcTxFees: ({ opReturnData, vault, sellAmount, adapterManager, pubkey, tradeFee }: {
1
+ import { UtxoBaseAdapter } from '@shapeshiftoss/chain-adapters';
2
+ import { QuoteFeeData, UtxoSupportedChainIds } from '../../../../../api';
3
+ export declare const getBtcTxFees: ({ opReturnData, vault, sellAmount, sellAdapter, pubkey, tradeFee }: {
5
4
  opReturnData: string;
6
5
  vault: string;
7
6
  sellAmount: string;
8
- adapterManager: ChainAdapterManager;
7
+ sellAdapter: UtxoBaseAdapter<UtxoSupportedChainIds>;
9
8
  pubkey: string;
10
9
  tradeFee: string;
11
- }) => Promise<QuoteFeeData<KnownChainIds.BitcoinMainnet>>;
10
+ }) => Promise<QuoteFeeData<UtxoSupportedChainIds>>;
@@ -1,27 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getBtcTxFees = void 0;
4
+ const types_1 = require("@shapeshiftoss/types");
4
5
  const api_1 = require("../../../../../api");
5
6
  const bignumber_1 = require("../../../../utils/bignumber");
6
- const getBtcTxFees = async ({ opReturnData, vault, sellAmount, adapterManager, pubkey, tradeFee }) => {
7
+ const getBtcTxFees = async ({ opReturnData, vault, sellAmount, sellAdapter, pubkey, tradeFee }) => {
7
8
  try {
8
- const adapter = adapterManager.get('bip122:000000000019d6689c085ae165831e93');
9
- if (!adapter)
10
- throw new api_1.SwapError(`[getBtcTxFees] - No chain adapter found for bip122:000000000019d6689c085ae165831e93.`, {
11
- code: api_1.SwapErrorTypes.UNSUPPORTED_CHAIN,
12
- details: { chainId: 'bip122:000000000019d6689c085ae165831e93' }
13
- });
14
- const feeDataOptions = await adapter.getFeeData({
9
+ const feeDataOptions = await sellAdapter.getFeeData({
15
10
  to: vault,
16
11
  value: sellAmount,
17
12
  chainSpecific: { pubkey, opReturnData }
18
13
  });
19
14
  const feeData = feeDataOptions['fast'];
15
+ // BCH specific hack:
16
+ // For some reason when sats per byte comes back as 1 (which is very common for bch)
17
+ // broadcast will fail because it thinks the intrinsic fee is too low
18
+ // it feels like possibly an off by-a-few-bytes bug with how we are using coinselect with opReturnData
19
+ // Bumping BCH fees here resolves this for now until we have time to find a better solution
20
+ const isFromBch = sellAdapter.getChainId() === types_1.KnownChainIds.BitcoinCashMainnet;
21
+ const feeMultiplier = isFromBch ? (0, bignumber_1.bn)(1.5) : (0, bignumber_1.bn)(1);
22
+ const fee = feeMultiplier.times(feeData.txFee).dp(0).toString();
23
+ const satsPerByte = feeMultiplier.times(feeData.chainSpecific.satoshiPerByte).dp(0).toString();
20
24
  return {
21
- fee: feeData.txFee,
25
+ fee,
22
26
  tradeFee,
23
27
  chainSpecific: {
24
- satsPerByte: feeData.chainSpecific.satoshiPerByte,
28
+ satsPerByte,
25
29
  byteCount: (0, bignumber_1.bn)(feeData.txFee)
26
30
  .dividedBy(feeData.chainSpecific.satoshiPerByte)
27
31
  .dp(0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshiftoss/swapper",
3
- "version": "9.8.0",
3
+ "version": "9.10.1",
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",