From d2a6c9c127b096e640770ce6892654771048ff89 Mon Sep 17 00:00:00 2001 From: Aleksandr Kraiz Date: Thu, 25 Aug 2022 11:00:28 +0400 Subject: [PATCH] Added getSwapInfo --- README.md | 70 ++++++++----- package.json | 2 +- src/OrionUnit/Exchange/getSwapInfo.ts | 138 ++++++++++++++++++++++++++ src/OrionUnit/Exchange/index.ts | 8 +- 4 files changed, 190 insertions(+), 28 deletions(-) create mode 100644 src/OrionUnit/Exchange/getSwapInfo.ts diff --git a/README.md b/README.md index 9412e13..f94eecd 100644 --- a/README.md +++ b/README.md @@ -103,33 +103,57 @@ orionUnit.exchange.deposit({ }); ``` -### Get swap market fee info +### Get swap info ```ts -orionUnit.exchange - .getSwapMarketFeeInfo({ - type: "exactSpend", - assetIn: "ORN", - assetOut: "USDT", - feeAsset: "ORN", - amount: 23.89045345, - options: { - // Optional - poolOnly: false, - }, - }) - .then(console.log); +const { swapInfo, fee } = await orionUnit.exchange.getSwapInfo({ + type: "exactSpend", + assetIn: "ORN", + assetOut: "USDT", + feeAsset: "ORN", + amount: 23.89045345, + options: { + // Optional + instantSettlement: true, + poolOnly: false, + }, +}); + +console.log(swapInfo); +console.log(fee); // { -// feeAsset: 'BNB', -// feeAssetAddress: '0x0000000000000000000000000000000000000000', -// feeAmount: '0.006' -// } - -// { -// feeAsset: 'ORN', -// feeAssetAddress: '0xf223eca06261145b3287a0fefd8cfad371c7eb34', -// feeAmount: '2.5910754708713146879833915507960091181362655' +// swapInfo: { +// id: 'e5d50b8e-ca82-4826-b454-3fa12b693c11', +// amountIn: 20, +// amountOut: 25.68, +// assetIn: 'ORN', +// assetOut: 'USDT', +// path: [ 'ORN', 'USDT' ], +// isThroughPoolOptimal: false, +// executionInfo: '...', +// orderInfo: { +// assetPair: 'ORN-USDT', +// side: 'SELL', +// amount: 20, +// safePrice: 1.284 +// }, +// exchanges: [ 'BINANCE' ], +// price: 1.284, +// minAmountOut: 12, +// minAmountIn: 9.4, +// marketPrice: 1.284, +// availableAmountOut: null, +// availableAmountIn: 20, +// marketAmountOut: 25.68, +// marketAmountIn: null, +// type: 'exactSpend' +// }, +// fee: { +// assetName: 'ORN', +// assetAddress: '0xf223eca06261145b3287a0fefd8cfad371c7eb34', +// amount: '1.0247136357221697138126003277499197658871168' +// } // } ``` diff --git a/package.json b/package.json index deedae4..80846f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.14.2", + "version": "0.15.0", "description": "Orion Protocol SDK", "main": "./lib/esm/index.js", "module": "./lib/esm/index.js", diff --git a/src/OrionUnit/Exchange/getSwapInfo.ts b/src/OrionUnit/Exchange/getSwapInfo.ts new file mode 100644 index 0000000..8db5af7 --- /dev/null +++ b/src/OrionUnit/Exchange/getSwapInfo.ts @@ -0,0 +1,138 @@ +import BigNumber from 'bignumber.js'; +import { ethers } from 'ethers'; +import { utils } from '../..'; +import { NATIVE_CURRENCY_PRECISION, SWAP_THROUGH_ORION_POOL_GAS_LIMIT } from '../../constants'; +import { OrionAggregator } from '../../services/OrionAggregator'; +import { OrionBlockchain } from '../../services/OrionBlockchain'; + +import simpleFetch from '../../simpleFetch'; +import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency'; + +export type GetSwapInfoParams = { + type: 'exactSpend' | 'exactReceive', + assetIn: string, + assetOut: string, + amount: BigNumber.Value, + feeAsset: string, + orionBlockchain: OrionBlockchain, + orionAggregator: OrionAggregator + options?: { + instantSettlement?: boolean, + poolOnly?: boolean, + } +} + +export default async function getSwapInfo({ + type, + assetIn, + assetOut, + amount, + feeAsset, + orionBlockchain, + orionAggregator, + options, +}: GetSwapInfoParams) { + if (amount === '') throw new Error('Amount can not be empty'); + if (assetIn === '') throw new Error('AssetIn can not be empty'); + if (assetOut === '') throw new Error('AssetOut can not be empty'); + if (feeAsset === '') throw new Error('Fee asset can not be empty'); + + const amountBN = new BigNumber(amount); + if (amountBN.isNaN()) throw new Error(`Amount '${amount.toString()}' is not a number`); + if (amountBN.lte(0)) throw new Error(`Amount '${amount.toString()}' should be greater than 0`); + + const { + assetToAddress, + } = await simpleFetch(orionBlockchain.getInfo)(); + const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress); + + const feeAssets = await simpleFetch(orionBlockchain.getTokensFee)(); + const pricesInOrn = await simpleFetch(orionBlockchain.getPrices)(); + const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)(); + + const gasPriceGwei = ethers.utils.formatUnits(gasPriceWei, 'gwei').toString(); + + const assetInAddress = assetToAddress[assetIn]; + if (!assetInAddress) throw new Error(`Asset '${assetIn}' not found`); + const feeAssetAddress = assetToAddress[feeAsset]; + if (!feeAssetAddress) throw new Error(`Fee asset '${feeAsset}' not found. Available assets: ${Object.keys(feeAssets).join(', ')}`); + + const swapInfo = await simpleFetch(orionAggregator.getSwapInfo)( + type, + assetIn, + assetOut, + amount.toString(), + options?.instantSettlement, + options?.poolOnly ? 'pools' : undefined, + ); + + if (swapInfo.orderInfo !== null && options?.poolOnly === true && options.poolOnly !== swapInfo.isThroughPoolOptimal) { + throw new Error(`Unexpected Orion Aggregator response. Please, contact support. Report swap request id: ${swapInfo.id}`); + } + + // if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) { + // throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`); + // } + + // if (swapInfo.type === 'exactSpend' && amountBN.lt(swapInfo.minAmountIn)) { + // throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`); + // } + + // if (swapInfo.orderInfo === null) throw new Error(swapInfo.executionInfo); + + let isThroughPoolOptimal: boolean; + if (options?.poolOnly) isThroughPoolOptimal = true; + else isThroughPoolOptimal = swapInfo.isThroughPoolOptimal; + + if (isThroughPoolOptimal) { + const transactionCost = ethers.BigNumber.from(SWAP_THROUGH_ORION_POOL_GAS_LIMIT).mul(gasPriceWei); + const denormalizedTransactionCost = utils.denormalizeNumber(transactionCost, NATIVE_CURRENCY_PRECISION); + + return { + swapInfo, + fee: { + assetName: nativeCryptocurrency, + assetAddress: ethers.constants.AddressZero, + amount: denormalizedTransactionCost.toString(), + }, + }; + } + + let feeAmount: string | undefined; + + if (swapInfo.orderInfo) { + const [baseAssetName] = swapInfo.orderInfo.assetPair.split('-'); + if (baseAssetName === undefined) throw new Error('Base asset name is undefined'); + const baseAssetAddress = assetToAddress[baseAssetName]; + if (!baseAssetAddress) throw new Error(`No asset address for ${baseAssetName}`); + + // Fee calculation + const baseAssetPriceInOrn = pricesInOrn?.[baseAssetAddress]; + if (!baseAssetPriceInOrn) throw new Error(`Base asset price ${baseAssetName} in ORN not found`); + const baseCurrencyPriceInOrn = pricesInOrn[ethers.constants.AddressZero]; + if (!baseCurrencyPriceInOrn) throw new Error('Base currency price in ORN not found'); + const feeAssetPriceInOrn = pricesInOrn[feeAssetAddress]; + if (!feeAssetPriceInOrn) throw new Error(`Fee asset price ${feeAsset} in ORN not found`); + const feePercent = feeAssets?.[feeAsset]; + if (!feePercent) throw new Error(`Fee asset ${feeAsset} not available`); + + const { totalFeeInFeeAsset } = utils.calculateFeeInFeeAsset( + swapInfo.orderInfo.amount, + feeAssetPriceInOrn, + baseAssetPriceInOrn, + baseCurrencyPriceInOrn, + gasPriceGwei, + feePercent, + ); + feeAmount = totalFeeInFeeAsset; + } + + return { + swapInfo, + fee: { + assetName: feeAsset, + assetAddress: feeAssetAddress, + amount: feeAmount, + }, + }; +} diff --git a/src/OrionUnit/Exchange/index.ts b/src/OrionUnit/Exchange/index.ts index f293970..7141f8d 100644 --- a/src/OrionUnit/Exchange/index.ts +++ b/src/OrionUnit/Exchange/index.ts @@ -1,13 +1,13 @@ import OrionUnit from '..'; import deposit, { DepositParams } from './deposit'; -import getSwapMarketFeeInfo, { GetSwapMarketInfoParams } from './getSwapMarketFeeInfo'; +import getSwapInfo, { GetSwapInfoParams } from './getSwapInfo'; import swapMarket, { SwapMarketParams } from './swapMarket'; import withdraw, { WithdrawParams } from './withdraw'; type PureSwapMarketParams= Omit type PureDepositParams = Omit type PureWithdrawParams = Omit -type PureGetSwapMarketInfoParams= Omit +type PureGetSwapMarketInfoParams= Omit export default class Exchange { private readonly orionUnit: OrionUnit; @@ -23,8 +23,8 @@ export default class Exchange { }); } - public getSwapMarketFeeInfo(params: PureGetSwapMarketInfoParams) { - return getSwapMarketFeeInfo({ + public getSwapInfo(params: PureGetSwapMarketInfoParams) { + return getSwapInfo({ orionAggregator: this.orionUnit.orionAggregator, orionBlockchain: this.orionUnit.orionBlockchain, ...params,