mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-16 16:21:32 +03:00
Calculate fee
This commit is contained in:
@@ -135,13 +135,12 @@ export default async function getSwapInfo({
|
||||
networkFeeInFeeAsset,
|
||||
} = calculateFeeInFeeAsset(
|
||||
swapInfo.orderInfo.amount,
|
||||
feeAssetPriceInQuoteAsset,
|
||||
baseAssetPriceInQuoteAsset,
|
||||
baseCurrencyPriceInQuoteAsset,
|
||||
gasPriceGwei,
|
||||
feePercent,
|
||||
baseAssetAddress,
|
||||
ethers.constants.AddressZero,
|
||||
feeAssetAddress,
|
||||
allPrices.prices,
|
||||
allPrices.prices
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -383,12 +383,11 @@ export default async function swapLimit({
|
||||
|
||||
const { serviceFeeInFeeAsset, networkFeeInFeeAsset, totalFeeInFeeAsset } = calculateFeeInFeeAsset(
|
||||
swapInfo.orderInfo.amount,
|
||||
feeAssetPriceInQuoteAsset,
|
||||
baseAssetPriceInQuoteAsset,
|
||||
baseCurrencyPriceInQuoteAsset,
|
||||
gasPriceGwei,
|
||||
feePercent,
|
||||
feeAsset,
|
||||
baseAssetAddress,
|
||||
ethers.constants.AddressZero,
|
||||
feeAssetAddress,
|
||||
allPrices.prices,
|
||||
);
|
||||
|
||||
@@ -405,7 +404,7 @@ export default async function swapLimit({
|
||||
name: feeAsset,
|
||||
address: feeAssetAddress,
|
||||
},
|
||||
amount: networkFeeInFeeAsset,
|
||||
amount: networkFeeInFeeAsset.toString(),
|
||||
spenderAddress: exchangeContractAddress,
|
||||
sources: getAvailableSources('network_fee', feeAssetAddress, 'aggregator'),
|
||||
});
|
||||
@@ -416,7 +415,7 @@ export default async function swapLimit({
|
||||
name: feeAsset,
|
||||
address: feeAssetAddress,
|
||||
},
|
||||
amount: serviceFeeInFeeAsset,
|
||||
amount: serviceFeeInFeeAsset.toString(),
|
||||
spenderAddress: exchangeContractAddress,
|
||||
sources: getAvailableSources('service_fee', feeAssetAddress, 'aggregator'),
|
||||
});
|
||||
|
||||
@@ -341,12 +341,11 @@ export default async function swapMarket({
|
||||
|
||||
const { serviceFeeInFeeAsset, networkFeeInFeeAsset, totalFeeInFeeAsset } = calculateFeeInFeeAsset(
|
||||
swapInfo.orderInfo.amount,
|
||||
feeAssetPriceInQuoteAsset,
|
||||
baseAssetPriceInQuoteAsset,
|
||||
baseCurrencyPriceInQuoteAsset,
|
||||
gasPriceGwei,
|
||||
feePercent,
|
||||
feeAsset,
|
||||
baseAssetAddress,
|
||||
ethers.constants.AddressZero,
|
||||
feeAssetAddress,
|
||||
allPrices.prices,
|
||||
);
|
||||
|
||||
@@ -363,7 +362,7 @@ export default async function swapMarket({
|
||||
name: feeAsset,
|
||||
address: feeAssetAddress,
|
||||
},
|
||||
amount: networkFeeInFeeAsset,
|
||||
amount: networkFeeInFeeAsset.toString(),
|
||||
spenderAddress: exchangeContractAddress,
|
||||
sources: getAvailableSources('network_fee', feeAssetAddress, 'aggregator'),
|
||||
});
|
||||
@@ -374,7 +373,7 @@ export default async function swapMarket({
|
||||
name: feeAsset,
|
||||
address: feeAssetAddress,
|
||||
},
|
||||
amount: serviceFeeInFeeAsset,
|
||||
amount: serviceFeeInFeeAsset.toString(),
|
||||
spenderAddress: exchangeContractAddress,
|
||||
sources: getAvailableSources('service_fee', feeAssetAddress, 'aggregator'),
|
||||
});
|
||||
|
||||
132
src/__tests__/fee.test.ts
Normal file
132
src/__tests__/fee.test.ts
Normal file
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
@@ -5,32 +5,33 @@ import calculateServiceFeeInFeeAsset from './calculateServiceFeeInFeeAsset.js';
|
||||
|
||||
const calculateFeeInFeeAsset = (
|
||||
amount: BigNumber.Value,
|
||||
feeAssetPrice: BigNumber.Value,
|
||||
baseAssetPrice: BigNumber.Value,
|
||||
baseCurrencyPrice: BigNumber.Value,
|
||||
gasPriceGwei: BigNumber.Value,
|
||||
feePercent: BigNumber.Value,
|
||||
feeAsset: string,
|
||||
assetPrices: Partial<Record<string, string>>,
|
||||
baseAssetAddress: string,
|
||||
baseCurrencyAddress: string,
|
||||
feeAssetAddress: string,
|
||||
prices: Partial<Record<string, string>>,
|
||||
) => {
|
||||
const feeAssetPriceInQuoteAsset = assetPrices[feeAsset];
|
||||
if (feeAssetPriceInQuoteAsset === undefined) throw Error('feeAssetPriceInQuoteAsset is undefined');
|
||||
|
||||
const feeAssetPriceInQuoteAssetBN = new BigNumber(feeAssetPriceInQuoteAsset);
|
||||
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,
|
||||
feeAssetPrice,
|
||||
baseAssetPrice,
|
||||
baseAssetAddress,
|
||||
feeAssetAddress,
|
||||
feePercent,
|
||||
feeAssetPriceInQuoteAssetBN
|
||||
prices,
|
||||
);
|
||||
const networkFeeInFeeAsset = calculateNetworkFeeInFeeAsset(
|
||||
gasPriceGwei,
|
||||
FILL_ORDERS_GAS_LIMIT,
|
||||
baseCurrencyPrice,
|
||||
feeAssetPrice,
|
||||
feeAssetPriceInQuoteAssetBN
|
||||
baseCurrencyAddress,
|
||||
feeAssetAddress,
|
||||
prices,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,20 +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,
|
||||
baseCurrencyPrice: BigNumber.Value,
|
||||
feeAssetPrice: BigNumber.Value,
|
||||
feeAssetPriceInQuoteAsset: BigNumber.Value,
|
||||
baseCurrencyAddress: string,
|
||||
feeAssetAddress: string,
|
||||
prices: Partial<Record<string, string>>
|
||||
) => {
|
||||
const networkFee = calculateNetworkFee(gasPriceGwei, gasLimit);
|
||||
|
||||
const networkFeeInQuoteAsset = new BigNumber(networkFee).multipliedBy(baseCurrencyPrice);
|
||||
const networkFeeInFeeAsset = networkFeeInQuoteAsset
|
||||
.div(new BigNumber(feeAssetPriceInQuoteAsset).multipliedBy(feeAssetPrice));
|
||||
|
||||
return networkFeeInFeeAsset.toString();
|
||||
return convertPrice(
|
||||
networkFee,
|
||||
baseCurrencyAddress, // from
|
||||
feeAssetAddress, // to
|
||||
prices
|
||||
);
|
||||
};
|
||||
|
||||
export default calculateNetworkFeeInFeeAsset;
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import convertPrice from './convertPrice.js';
|
||||
|
||||
export default function calculateServiceFeeInFeeAsset(
|
||||
amount: BigNumber.Value,
|
||||
feeAssetPrice: BigNumber.Value,
|
||||
baseAssetPrice: BigNumber.Value,
|
||||
baseAssetAddress: string,
|
||||
feeAssetAddress: string,
|
||||
feePercent: BigNumber.Value,
|
||||
feeAssetPriceInQuoteAsset: BigNumber.Value,
|
||||
prices: Partial<Record<string, string>>
|
||||
) {
|
||||
const result = new BigNumber(amount)
|
||||
.multipliedBy(new BigNumber(feePercent).div(100))
|
||||
.multipliedBy(baseAssetPrice)
|
||||
.div(new BigNumber(feeAssetPriceInQuoteAsset).multipliedBy(feeAssetPrice))
|
||||
.toString();
|
||||
const feeAmount = new BigNumber(amount).multipliedBy(new BigNumber(feePercent).div(100));
|
||||
|
||||
return result;
|
||||
const feeAssetAmount = convertPrice(
|
||||
feeAmount,
|
||||
baseAssetAddress,
|
||||
feeAssetAddress,
|
||||
prices
|
||||
);
|
||||
|
||||
return feeAssetAmount;
|
||||
}
|
||||
|
||||
26
src/utils/convertPrice.ts
Normal file
26
src/utils/convertPrice.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
export default function convertPrice(
|
||||
amount: BigNumber.Value,
|
||||
assetInAddress: string,
|
||||
assetOutAddress: string,
|
||||
prices: Partial<Record<string, string>> // 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;
|
||||
}
|
||||
Reference in New Issue
Block a user