diff --git a/package.json b/package.json index 501882e..e075a79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orionprotocol/sdk", - "version": "0.19.41", + "version": "0.19.42-rc5", "description": "Orion Protocol SDK", "main": "./lib/index.cjs", "module": "./lib/index.js", @@ -108,4 +108,4 @@ "overrides": { "tsconfig-paths": "^4.0.0" } -} +} \ No newline at end of file diff --git a/src/Unit/Exchange/getSwapInfo.ts b/src/Unit/Exchange/getSwapInfo.ts index 08108d1..e6c0f9e 100644 --- a/src/Unit/Exchange/getSwapInfo.ts +++ b/src/Unit/Exchange/getSwapInfo.ts @@ -46,7 +46,7 @@ export default async function getSwapInfo({ const nativeCryptocurrencyName = getNativeCryptocurrencyName(assetToAddress); const feeAssets = await simpleFetch(blockchainService.getTokensFee)(); - const pricesInOrn = await simpleFetch(blockchainService.getPrices)(); + const allPrices = await simpleFetch(blockchainService.getPricesWithQuoteAsset)(); const gasPriceWei = await simpleFetch(blockchainService.getGasPriceWei)(); const gasPriceGwei = ethers.utils.formatUnits(gasPriceWei, 'gwei').toString(); @@ -121,12 +121,6 @@ export default async function getSwapInfo({ if (baseAssetAddress === undefined) throw new Error(`No asset address for ${baseAssetName}`); // Fee calculation - const baseAssetPriceInOrn = pricesInOrn[baseAssetAddress]; - if (baseAssetPriceInOrn === undefined) throw new Error(`Base asset price ${baseAssetName} in ORN not found`); - const baseCurrencyPriceInOrn = pricesInOrn[ethers.constants.AddressZero]; - if (baseCurrencyPriceInOrn === undefined) throw new Error('Base currency price in ORN not found'); - const feeAssetPriceInOrn = pricesInOrn[feeAssetAddress]; - if (feeAssetPriceInOrn === undefined) throw new Error(`Fee asset price ${feeAsset} in ORN not found`); const feePercent = feeAssets[feeAsset]; if (feePercent === undefined) throw new Error(`Fee asset ${feeAsset} not available`); @@ -135,11 +129,12 @@ export default async function getSwapInfo({ networkFeeInFeeAsset, } = calculateFeeInFeeAsset( swapInfo.orderInfo.amount, - feeAssetPriceInOrn, - baseAssetPriceInOrn, - baseCurrencyPriceInOrn, gasPriceGwei, feePercent, + baseAssetAddress, + ethers.constants.AddressZero, + feeAssetAddress, + allPrices.prices ); return { diff --git a/src/Unit/Exchange/swapLimit.ts b/src/Unit/Exchange/swapLimit.ts index a3520eb..3b62706 100644 --- a/src/Unit/Exchange/swapLimit.ts +++ b/src/Unit/Exchange/swapLimit.ts @@ -90,7 +90,7 @@ export default async function swapLimit({ const exchangeContract = Exchange__factory.connect(exchangeContractAddress, provider); const feeAssets = await simpleFetch(blockchainService.getTokensFee)(); - const pricesInOrn = await simpleFetch(blockchainService.getPrices)(); + const allPrices = await simpleFetch(blockchainService.getPricesWithQuoteAsset)(); const gasPriceWei = await simpleFetch(blockchainService.getGasPriceWei)(); const { factories } = await simpleFetch(blockchainService.getPoolsConfig)(); const poolExchangesList = factories !== undefined ? Object.keys(factories) : []; @@ -372,22 +372,17 @@ export default async function swapLimit({ }); // Fee calculation - const baseAssetPriceInOrn = pricesInOrn[baseAssetAddress]; - if (baseAssetPriceInOrn === undefined) throw new Error(`Base asset price ${baseAssetName} in ORN not found`); - const baseCurrencyPriceInOrn = pricesInOrn[ethers.constants.AddressZero]; - if (baseCurrencyPriceInOrn === undefined) throw new Error('Base currency price in ORN not found'); - const feeAssetPriceInOrn = pricesInOrn[feeAssetAddress]; - if (feeAssetPriceInOrn === undefined) throw new Error(`Fee asset price ${feeAsset} in ORN not found`); const feePercent = feeAssets[feeAsset]; if (feePercent === undefined) throw new Error(`Fee asset ${feeAsset} not available`); const { serviceFeeInFeeAsset, networkFeeInFeeAsset, totalFeeInFeeAsset } = calculateFeeInFeeAsset( swapInfo.orderInfo.amount, - feeAssetPriceInOrn, - baseAssetPriceInOrn, - baseCurrencyPriceInOrn, gasPriceGwei, feePercent, + baseAssetAddress, + ethers.constants.AddressZero, + feeAssetAddress, + allPrices.prices, ); if (feeAsset === assetOut) { @@ -403,7 +398,7 @@ export default async function swapLimit({ name: feeAsset, address: feeAssetAddress, }, - amount: networkFeeInFeeAsset, + amount: networkFeeInFeeAsset.toString(), spenderAddress: exchangeContractAddress, sources: getAvailableSources('network_fee', feeAssetAddress, 'aggregator'), }); @@ -414,7 +409,7 @@ export default async function swapLimit({ name: feeAsset, address: feeAssetAddress, }, - amount: serviceFeeInFeeAsset, + amount: serviceFeeInFeeAsset.toString(), spenderAddress: exchangeContractAddress, sources: getAvailableSources('service_fee', feeAssetAddress, 'aggregator'), }); diff --git a/src/Unit/Exchange/swapMarket.ts b/src/Unit/Exchange/swapMarket.ts index c90d22d..2ac8e2f 100644 --- a/src/Unit/Exchange/swapMarket.ts +++ b/src/Unit/Exchange/swapMarket.ts @@ -75,7 +75,7 @@ export default async function swapMarket({ const exchangeContract = Exchange__factory.connect(exchangeContractAddress, provider); const feeAssets = await simpleFetch(blockchainService.getTokensFee)(); - const pricesInOrn = await simpleFetch(blockchainService.getPrices)(); + const allPrices = await simpleFetch(blockchainService.getPricesWithQuoteAsset)(); const gasPriceWei = await simpleFetch(blockchainService.getGasPriceWei)(); const { factories } = await simpleFetch(blockchainService.getPoolsConfig)(); const poolExchangesList = factories !== undefined ? Object.keys(factories) : []; @@ -330,22 +330,17 @@ export default async function swapMarket({ }); // Fee calculation - const baseAssetPriceInOrn = pricesInOrn[baseAssetAddress]; - if (baseAssetPriceInOrn === undefined) throw new Error(`Base asset price ${baseAssetName} in ORN not found`); - const baseCurrencyPriceInOrn = pricesInOrn[ethers.constants.AddressZero]; - if (baseCurrencyPriceInOrn === undefined) throw new Error('Base currency price in ORN not found'); - const feeAssetPriceInOrn = pricesInOrn[feeAssetAddress]; - if (feeAssetPriceInOrn === undefined) throw new Error(`Fee asset price ${feeAsset} in ORN not found`); const feePercent = feeAssets[feeAsset]; if (feePercent === undefined) throw new Error(`Fee asset ${feeAsset} not available`); const { serviceFeeInFeeAsset, networkFeeInFeeAsset, totalFeeInFeeAsset } = calculateFeeInFeeAsset( swapInfo.orderInfo.amount, - feeAssetPriceInOrn, - baseAssetPriceInOrn, - baseCurrencyPriceInOrn, gasPriceGwei, feePercent, + baseAssetAddress, + ethers.constants.AddressZero, + feeAssetAddress, + allPrices.prices, ); if (feeAsset === assetOut) { @@ -361,7 +356,7 @@ export default async function swapMarket({ name: feeAsset, address: feeAssetAddress, }, - amount: networkFeeInFeeAsset, + amount: networkFeeInFeeAsset.toString(), spenderAddress: exchangeContractAddress, sources: getAvailableSources('network_fee', feeAssetAddress, 'aggregator'), }); @@ -372,7 +367,7 @@ export default async function swapMarket({ name: feeAsset, address: feeAssetAddress, }, - amount: serviceFeeInFeeAsset, + amount: serviceFeeInFeeAsset.toString(), spenderAddress: exchangeContractAddress, sources: getAvailableSources('service_fee', feeAssetAddress, 'aggregator'), }); diff --git a/src/__tests__/fee.test.ts b/src/__tests__/fee.test.ts new file mode 100644 index 0000000..eec8eeb --- /dev/null +++ b/src/__tests__/fee.test.ts @@ -0,0 +1,132 @@ +/* eslint-disable max-len */ +import { ethers } from 'ethers'; +import calculateFeeInFeeAsset from '../utils/calculateFeeInFeeAsset.js'; + +describe('Fee calculation', () => { + test('Dynamic quote asset', async () => { + const ornPrices = { + '0xf223eca06261145b3287a0fefd8cfad371c7eb34': '1', + '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744': '1.57084511467169337103', + '0x0000000000000000000000000000000000000000': '386.899151743638077284689', + '0x4b9bfdde9dab73e6e2d2dc2ef55298c24588cce0': '1.0876374489475337731674617', + '0x108f6f65f5c258678c366383840c96679e1a0fbe': '45871.190700596921143469648', + '0xdf22ff6f5bf90e92f7074de1fa2e29dafe0087be': '2881.5111530003141690163011', + '0x03a2152a9aaacb96b82fdf03de0d643c3ecb28a7': '355.016417214333651756801351013730316783167', + '0x61a072dcc55d64da4cb910cfbc943cc3d42c2a1d': '1.571473452717562048378412', + '0xcd9f77d618dfdffdaed7f8023c34eb35ea86bf2b': '1.5712221174992145774390472', + '0x6cc5e1598f95e052ed021bbb0a1990ba485cdcce': '0.0374332390826264530316449', + '0x78867bbeef44f2326bf8ddd1941a4439382ef2a7': '1.566674947779146776937419747383627421398778019', + '0x3745553f445397dfbcb622192792491e6df67510': '130.71996892903857709354866649329101687558281412962786572513363298758635752124474785635955692', + '0x8447cda1d129ec040d094c011c3ac8b19f5c693b': '27.1071613903560920438050171526477214707556556810141836390412796594504062216941295811483058089915710982545331028', + '0x4fe5767ca2fe999e760dfd358614514a3219889a': '1.70174254929807881257423716231112230510407656790753360661237', + '0x2220b40a89efa6b0df48ed79d79203477132e371': '130.3525444094548794105350358066444422201669189500289668961281052404545221556', + '0xb0827f392d8c0852c0c6454bb9640cb989dcb93b': '64.400713005859731411119486616035862848696163238405378922467895263156517684272858763674589252508', + '0x47aac1c87e618915db97626f3960512515ce9e09': '437.31331069646443999899330475877673885912', + '0xc5d535dac5e3d56505fe331e2eba9842bbd19ee2': '13.6960254871019251168696037670276578586589975026010466153339830092462843027905620215383565464144808', + '0x0021588ea52535343f32e1b36c717e6233061bd4': '546.161187756536499998742736945784452977', + '0xdd301d6bb42be154bd940084ae86e14ca31436c3': '7.473234117200213999982796615062205107372', + '0xb9f5146b403bf70907c512c5e4d09e0565f577d1': '3.27853649699826360702741064013340499717660552707269386251', + '0xffd86d9ad101f1bce1090761f1574f991e6f49ee': '1.10275456361938750235372913100208822652' + }; + + const usdtPrices = { + '0xf223eca06261145b3287a0fefd8cfad371c7eb34': '0.6366000000000000000014654532', + '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744': '1', + '0x0000000000000000000000000000000000000000': '246.3', + '0x4b9bfdde9dab73e6e2d2dc2ef55298c24588cce0': '0.69239', + '0x108f6f65f5c258678c366383840c96679e1a0fbe': '29201.6', + '0xdf22ff6f5bf90e92f7074de1fa2e29dafe0087be': '1834.37', + '0x03a2152a9aaacb96b82fdf03de0d643c3ecb28a7': '226.0034511986448027089', + '0x61a072dcc55d64da4cb910cfbc943cc3d42c2a1d': '1.0004', + '0xcd9f77d618dfdffdaed7f8023c34eb35ea86bf2b': '1.00024', + '0x6cc5e1598f95e052ed021bbb0a1990ba485cdcce': '0.02383', + '0x78867bbeef44f2326bf8ddd1941a4439382ef2a7': '0.9973452717562048382006573', + '0x3745553f445397dfbcb622192792491e6df67510': '83.21633222022595817794464509', + '0x8447cda1d129ec040d094c011c3ac8b19f5c693b': '17.25641894110068819512599820', + '0x4fe5767ca2fe999e760dfd358614514a3219889a': '1.083329306883156972087253202', + '0x2220b40a89efa6b0df48ed79d79203477132e371': '82.98242977105897623293762935', + '0xb0827f392d8c0852c0c6454bb9640cb989dcb93b': '40.99749389953030501641304141', + '0x47aac1c87e618915db97626f3960512515ce9e09': '278.393653589369262504', + '0xc5d535dac5e3d56505fe331e2eba9842bbd19ee2': '8.718889825089085529419260642', + '0x0021588ea52535343f32e1b36c717e6233061bd4': '347.6862121258111359', + '0xdd301d6bb42be154bd940084ae86e14ca31436c3': '4.7574608390096562324', + '0xb9f5146b403bf70907c512c5e4d09e0565f577d1': '2.087116333989094612238454155', + '0xffd86d9ad101f1bce1090761f1574f991e6f49ee': '0.702013555200102084' + }; + + const bnbPrices = { + '0xf223eca06261145b3287a0fefd8cfad371c7eb34': '0.002584652862362972', + '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744': '0.004060089321965083', + '0x0000000000000000000000000000000000000000': '1.0', + '0x4b9bfdde9dab73e6e2d2dc2ef55298c24588cce0': '0.002811165245635404', + '0x108f6f65f5c258678c366383840c96679e1a0fbe': '118.56110434429557', + '0xdf22ff6f5bf90e92f7074de1fa2e29dafe0087be': '7.44770604953309', + '0x03a2152a9aaacb96b82fdf03de0d643c3ecb28a7': '0.9175941989388746', + '0x61a072dcc55d64da4cb910cfbc943cc3d42c2a1d': '0.004061713357693869', + '0xcd9f77d618dfdffdaed7f8023c34eb35ea86bf2b': '0.004061063743402355', + '0x6cc5e1598f95e052ed021bbb0a1990ba485cdcce': '9.675192854242793e-05', + '0x78867bbeef44f2326bf8ddd1941a4439382ef2a7': '0.004049310888169732', + '0x3745553f445397dfbcb622192792491e6df67510': '0.33786574186043833', + '0x8447cda1d129ec040d094c011c3ac8b19f5c693b': '0.07006260227811892', + '0x4fe5767ca2fe999e760dfd358614514a3219889a': '0.004398413751048141', + '0x2220b40a89efa6b0df48ed79d79203477132e371': '0.33691607702419396', + '0xb0827f392d8c0852c0c6454bb9640cb989dcb93b': '0.16645348720881162', + '0x47aac1c87e618915db97626f3960512515ce9e09': '1.1303031002410446', + '0xc5d535dac5e3d56505fe331e2eba9842bbd19ee2': '0.03539947147823421', + '0x0021588ea52535343f32e1b36c717e6233061bd4': '1.4116370772464926', + '0xdd301d6bb42be154bd940084ae86e14ca31436c3': '0.01931571595213015', + '0xb9f5146b403bf70907c512c5e4d09e0565f577d1': '0.008473878741328034', + '0xffd86d9ad101f1bce1090761f1574f991e6f49ee': '0.00285023773934268' + }; + + const amount = 1000; + const gasPriceGwei = 3; + const feePercent = 0.2; + const baseAssetAddress = '0xcb2951e90d8dcf16e1fa84ac0c83f48906d6a744'; + const baseCurrencyAddress = ethers.constants.AddressZero; + const feeAssetAddress = '0xf223eca06261145b3287a0fefd8cfad371c7eb34'; + const { totalFeeInFeeAsset: ornTotalFee } = calculateFeeInFeeAsset( + amount, + gasPriceGwei, + feePercent, + baseAssetAddress, + baseCurrencyAddress, + feeAssetAddress, + ornPrices + ); + + const { totalFeeInFeeAsset: bnbTotalFee } = calculateFeeInFeeAsset( + amount, + gasPriceGwei, + feePercent, + baseAssetAddress, + baseCurrencyAddress, + feeAssetAddress, + bnbPrices + ); + const { totalFeeInFeeAsset: usdtTotalFee } = calculateFeeInFeeAsset( + amount, + gasPriceGwei, + feePercent, + baseAssetAddress, + baseCurrencyAddress, + feeAssetAddress, + usdtPrices + ); + + const ornTotalFeeNum = parseFloat(ornTotalFee); + const bnbTotalFeeNum = parseFloat(bnbTotalFee); + const usdtTotalFeeNum = parseFloat(usdtTotalFee); + + const average = (ornTotalFeeNum + bnbTotalFeeNum + usdtTotalFeeNum) / 3; + const ornDiff = Math.abs(ornTotalFeeNum - average); + const bnbDiff = Math.abs(bnbTotalFeeNum - average); + const usdtDiff = Math.abs(usdtTotalFeeNum - average); + + const acceptableDiffPercent = 0.01; + + expect(ornDiff / average).toBeLessThan(acceptableDiffPercent); + expect(bnbDiff / average).toBeLessThan(acceptableDiffPercent); + expect(usdtDiff / average).toBeLessThan(acceptableDiffPercent); + }); +}); diff --git a/src/services/Aggregator/schemas/swapInfoSchema.ts b/src/services/Aggregator/schemas/swapInfoSchema.ts index 26b7947..cc2166e 100644 --- a/src/services/Aggregator/schemas/swapInfoSchema.ts +++ b/src/services/Aggregator/schemas/swapInfoSchema.ts @@ -39,7 +39,7 @@ const swapInfoBase = z.object({ orderInfo: orderInfoSchema, isThroughPoolOrCurve: z.boolean(), }).array(), - anm: z.record(z.string()).optional(), // address to ERC20 names + assetNameMapping: z.record(z.string()).optional(), // address to ERC20 names }); const swapInfoByAmountIn = swapInfoBase.extend({ diff --git a/src/services/Aggregator/ws/index.ts b/src/services/Aggregator/ws/index.ts index 06ca450..930b6f7 100644 --- a/src/services/Aggregator/ws/index.ts +++ b/src/services/Aggregator/ws/index.ts @@ -267,7 +267,7 @@ class AggregatorWS { if ('payload' in subscription) { if (typeof subscription.payload === 'string') { subRequest['S'] = subscription.payload; - } else { // SwapInfoSubscriptionPayload | FuturesTradeInfoPayload + } else { // SwapInfoSubscriptionPayload subRequest['S'] = { d: id, ...subscription.payload, diff --git a/src/services/BlockchainService/index.ts b/src/services/BlockchainService/index.ts index 9912b7f..15f88a0 100644 --- a/src/services/BlockchainService/index.ts +++ b/src/services/BlockchainService/index.ts @@ -14,6 +14,7 @@ import { governancePoolsSchema, governancePoolSchema, governanceChainsInfoSchema, + pricesWithQuoteAssetSchema, } from './schemas/index.js'; import type redeemOrderSchema from '../Aggregator/schemas/redeemOrderSchema.js'; import { sourceAtomicHistorySchema, targetAtomicHistorySchema } from './schemas/atomicHistorySchema.js'; @@ -84,6 +85,7 @@ class BlockchainService { this.getPoolsV3Info = this.getPoolsV3Info.bind(this); this.getHistory = this.getHistory.bind(this); this.getPrices = this.getPrices.bind(this); + this.getPricesWithQuoteAsset = this.getPricesWithQuoteAsset.bind(this); this.getTokensFee = this.getTokensFee.bind(this); this.getGasPriceWei = this.getGasPriceWei.bind(this); this.checkFreeRedeemAvailable = this.checkFreeRedeemAvailable.bind(this); @@ -220,6 +222,12 @@ class BlockchainService { { headers: this.basicAuthHeaders } ); + getPricesWithQuoteAsset = () => fetchWithValidation( + `${this.apiUrl}/api/quotedPrices`, + pricesWithQuoteAssetSchema, + { headers: this.basicAuthHeaders } + ); + getTokensFee = () => fetchWithValidation( `${this.apiUrl}/api/tokensFee`, z.record(z.string()).transform(makePartial), diff --git a/src/services/BlockchainService/schemas/index.ts b/src/services/BlockchainService/schemas/index.ts index 7ecbda5..80b82f3 100644 --- a/src/services/BlockchainService/schemas/index.ts +++ b/src/services/BlockchainService/schemas/index.ts @@ -17,3 +17,4 @@ export { default as governancePoolsSchema } from './governancePoolsSchema.js'; export { default as governancePoolSchema } from './governancePoolSchema.js'; export { default as governanceChainsInfoSchema } from './governanceChainsInfoSchema.js'; export { default as poolsV3InfoSchema } from './poolsV3InfoSchema.js'; +export { pricesWithQuoteAssetSchema } from './pricesWithQuoteAssetSchema.js'; diff --git a/src/services/BlockchainService/schemas/pricesWithQuoteAssetSchema.ts b/src/services/BlockchainService/schemas/pricesWithQuoteAssetSchema.ts new file mode 100644 index 0000000..4fcaef0 --- /dev/null +++ b/src/services/BlockchainService/schemas/pricesWithQuoteAssetSchema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; +import { makePartial } from '../../../utils/index.js'; + +export const pricesWithQuoteAssetSchema = z.object({ + quoteAsset: z.string(), + quoteAssetAddress: z.string(), + prices: z.record(z.string()).transform(makePartial) +}); diff --git a/src/utils/calculateFeeInFeeAsset.ts b/src/utils/calculateFeeInFeeAsset.ts index e4a6da9..e4dd53d 100644 --- a/src/utils/calculateFeeInFeeAsset.ts +++ b/src/utils/calculateFeeInFeeAsset.ts @@ -5,23 +5,33 @@ import calculateServiceFeeInFeeAsset from './calculateServiceFeeInFeeAsset.js'; const calculateFeeInFeeAsset = ( amount: BigNumber.Value, - feeAssetPriceInServiceToken: BigNumber.Value, - baseAssetPriceInServiceToken: BigNumber.Value, - baseCurrencyPriceInServiceToken: BigNumber.Value, gasPriceGwei: BigNumber.Value, feePercent: BigNumber.Value, + baseAssetAddress: string, + baseCurrencyAddress: string, + feeAssetAddress: string, + prices: Partial>, ) => { + const feeAssetPrice = prices[feeAssetAddress]; + if (feeAssetPrice === undefined) throw Error(`Fee asset price not found. Available prices: ${Object.keys(prices).join(', ')}`); + const baseAssetPrice = prices[baseAssetAddress]; + if (baseAssetPrice === undefined) throw Error(`Base asset price not found. Available prices: ${Object.keys(prices).join(', ')}`); + const baseCurrencyPrice = prices[baseCurrencyAddress]; // ETH, BNB, MATIC, etc. + if (baseCurrencyPrice === undefined) throw Error(`Base currency price not found. Available prices: ${Object.keys(prices).join(', ')}`); + const serviceFeeInFeeAsset = calculateServiceFeeInFeeAsset( amount, - feeAssetPriceInServiceToken, - baseAssetPriceInServiceToken, + baseAssetAddress, + feeAssetAddress, feePercent, + prices, ); const networkFeeInFeeAsset = calculateNetworkFeeInFeeAsset( gasPriceGwei, FILL_ORDERS_GAS_LIMIT, - baseCurrencyPriceInServiceToken, - feeAssetPriceInServiceToken, + baseCurrencyAddress, + feeAssetAddress, + prices, ); return { diff --git a/src/utils/calculateNetworkFeeInFeeAsset.ts b/src/utils/calculateNetworkFeeInFeeAsset.ts index 23b611c..8e399eb 100644 --- a/src/utils/calculateNetworkFeeInFeeAsset.ts +++ b/src/utils/calculateNetworkFeeInFeeAsset.ts @@ -1,22 +1,22 @@ -import { BigNumber } from 'bignumber.js'; +import type { BigNumber } from 'bignumber.js'; import calculateNetworkFee from './calculateNetworkFee.js'; +import convertPrice from './convertPrice.js'; const calculateNetworkFeeInFeeAsset = ( gasPriceGwei: BigNumber.Value, gasLimit: BigNumber.Value, - baseCurrencyPriceInServiceToken: BigNumber.Value, - feeAssetPriceInServiceToken: BigNumber.Value, + baseCurrencyAddress: string, + feeAssetAddress: string, + prices: Partial> ) => { const networkFee = calculateNetworkFee(gasPriceGwei, gasLimit); - const networkFeeInServiceToken = new BigNumber(networkFee).multipliedBy(baseCurrencyPriceInServiceToken); - const networkFeeInFeeAsset = networkFeeInServiceToken - .multipliedBy( - new BigNumber(1) - .div(feeAssetPriceInServiceToken), - ); - - return networkFeeInFeeAsset.toString(); + return convertPrice( + networkFee, + baseCurrencyAddress, // from + feeAssetAddress, // to + prices + ); }; export default calculateNetworkFeeInFeeAsset; diff --git a/src/utils/calculateServiceFeeInFeeAsset.ts b/src/utils/calculateServiceFeeInFeeAsset.ts index 4cad980..d329d9e 100644 --- a/src/utils/calculateServiceFeeInFeeAsset.ts +++ b/src/utils/calculateServiceFeeInFeeAsset.ts @@ -1,16 +1,21 @@ import { BigNumber } from 'bignumber.js'; +import convertPrice from './convertPrice.js'; export default function calculateServiceFeeInFeeAsset( amount: BigNumber.Value, - feeAssetPriceInServiceToken: BigNumber.Value, - baseAssetPriceInServiceToken: BigNumber.Value, + baseAssetAddress: string, + feeAssetAddress: string, feePercent: BigNumber.Value, + prices: Partial> ) { - const result = new BigNumber(amount) - .multipliedBy(new BigNumber(feePercent).div(100)) - .multipliedBy(baseAssetPriceInServiceToken) - .multipliedBy(new BigNumber(1).div(feeAssetPriceInServiceToken)) - .toString(); + const feeAmount = new BigNumber(amount).multipliedBy(new BigNumber(feePercent).div(100)); - return result; + const feeAssetAmount = convertPrice( + feeAmount, + baseAssetAddress, + feeAssetAddress, + prices + ); + + return feeAssetAmount; } diff --git a/src/utils/convertPrice.ts b/src/utils/convertPrice.ts new file mode 100644 index 0000000..95415bb --- /dev/null +++ b/src/utils/convertPrice.ts @@ -0,0 +1,26 @@ +import { BigNumber } from 'bignumber.js'; + +export default function convertPrice( + amount: BigNumber.Value, + assetInAddress: string, + assetOutAddress: string, + prices: Partial> // quoted in quoteAsset. [address]: priceQuotedInQuoteAsset +) { + const assetInPrice = prices[assetInAddress]; + if (assetInPrice === undefined) throw Error('assetInPrice is undefined'); + + const assetOutPrice = prices[assetOutAddress]; + if (assetOutPrice === undefined) throw Error('assetOutPrice is undefined'); + + const assetInPriceBN = new BigNumber(assetInPrice); + const assetOutPriceBN = new BigNumber(assetOutPrice); + + const assetInAmountBN = new BigNumber(amount); + + const assetInAmountInQuoteAsset = assetInAmountBN.multipliedBy(assetInPriceBN); + const assetInAmountInQuoteAssetBN = new BigNumber(assetInAmountInQuoteAsset); + + const assetOutAmountInQuoteAsset = assetInAmountInQuoteAssetBN.dividedBy(assetOutPriceBN); + + return assetOutAmountInQuoteAsset; +}