mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-16 16:21:32 +03:00
merged with develop
This commit is contained in:
@@ -108,4 +108,4 @@
|
||||
"overrides": {
|
||||
"tsconfig-paths": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/Orion/bridge/index.ts
Normal file
100
src/Orion/bridge/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { ethers } from 'ethers';
|
||||
import {
|
||||
type AtomicSwap, type SupportedChainId,
|
||||
type Unit, INTERNAL_PROTOCOL_PRECISION
|
||||
} from '../../index.js';
|
||||
import getHistoryExt from './getHistory.js';
|
||||
import swapExt from './swap.js';
|
||||
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
export default class Bridge {
|
||||
constructor(
|
||||
private readonly unitsArray: Unit[],
|
||||
) {}
|
||||
|
||||
async getMergedHistory(
|
||||
externalStoredAtomicSwaps: AtomicSwap[],
|
||||
walletAddress: string,
|
||||
) {
|
||||
const bridgeHistory = await this.getHistory(walletAddress);
|
||||
|
||||
return Object.values(bridgeHistory).map((atomicSwap) => {
|
||||
if (atomicSwap === undefined) throw new Error('No atomic swap');
|
||||
|
||||
const {
|
||||
secretHash,
|
||||
amountToReceive,
|
||||
amountToSpend,
|
||||
targetChainId,
|
||||
asset,
|
||||
sourceChainId,
|
||||
sender,
|
||||
transactions,
|
||||
expiration,
|
||||
creationDate,
|
||||
} = atomicSwap;
|
||||
|
||||
const localSwap = externalStoredAtomicSwaps.find(
|
||||
(swap) => secretHash === swap.secretHash,
|
||||
);
|
||||
|
||||
const amount = amountToReceive ?? amountToSpend ?? 0;
|
||||
|
||||
// Checking if transaction hash from blockchain is different from the same in local storage
|
||||
// and changing it to the correct one
|
||||
|
||||
let assetName = asset;
|
||||
|
||||
// LEGACY. Some old atomic swaps have address instead of asset name. Here we handle this case
|
||||
if (asset.includes('0x')) {
|
||||
assetName = '—'; // We don't want to display address even if we can't find asset name
|
||||
}
|
||||
|
||||
return {
|
||||
localSwap,
|
||||
sourceNetwork: sourceChainId,
|
||||
targetNetwork: targetChainId,
|
||||
amount: new BigNumber(amount)
|
||||
.multipliedBy(new BigNumber(10).pow(INTERNAL_PROTOCOL_PRECISION))
|
||||
.toString(),
|
||||
walletAddress: sender,
|
||||
secretHash,
|
||||
lockTransactionHash: transactions?.lock,
|
||||
refundTransactionHash: transactions?.refund,
|
||||
asset: assetName,
|
||||
expiration:
|
||||
expiration?.lock ?? creationDate.getTime() + 60 * 60 * 24 * 4, // Or default 4 days
|
||||
creationDate: creationDate.getTime(),
|
||||
redeemOrder: atomicSwap.redeemOrder,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getHistory(address: string, limit = 1000) {
|
||||
return getHistoryExt(this.unitsArray, address, limit);
|
||||
}
|
||||
|
||||
swap(
|
||||
assetName: string,
|
||||
amount: BigNumber.Value,
|
||||
sourceChain: SupportedChainId,
|
||||
targetChain: SupportedChainId,
|
||||
signer: ethers.Signer,
|
||||
options: {
|
||||
autoApprove?: boolean
|
||||
logger?: (message: string) => void
|
||||
withdrawToWallet?: boolean
|
||||
}
|
||||
) {
|
||||
return swapExt({
|
||||
amount,
|
||||
assetName,
|
||||
sourceChain,
|
||||
targetChain,
|
||||
signer,
|
||||
unitsArray: this.unitsArray,
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,10 @@ import {
|
||||
import getNativeCryptocurrencyName from '../../utils/getNativeCryptocurrencyName.js';
|
||||
import { denormalizeNumber, generateSecret, normalizeNumber, toUpperCase } from '../../utils/index.js';
|
||||
import type { SupportedChainId } from '../../types.js';
|
||||
import type Orion from '../index.js';
|
||||
import type { z } from 'zod';
|
||||
import type { placeAtomicSwapSchema } from '../../services/Aggregator/schemas/index.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
import type { Unit } from '../../index.js';
|
||||
|
||||
type Params = {
|
||||
assetName: string
|
||||
@@ -25,7 +25,7 @@ type Params = {
|
||||
sourceChain: SupportedChainId
|
||||
targetChain: SupportedChainId
|
||||
signer: ethers.Signer
|
||||
orion: Orion
|
||||
unitsArray: Unit[]
|
||||
options?: {
|
||||
withdrawToWallet?: boolean // By default, the transfer amount remains in the exchange contract
|
||||
autoApprove?: boolean
|
||||
@@ -40,7 +40,7 @@ export default async function swap({
|
||||
targetChain,
|
||||
signer,
|
||||
options,
|
||||
orion
|
||||
unitsArray,
|
||||
}: Params) {
|
||||
const startProcessingTime = Date.now();
|
||||
if (amount === '') throw new Error('Amount can not be empty');
|
||||
@@ -50,8 +50,11 @@ export default async function swap({
|
||||
if (amountBN.isNaN()) throw new Error(`Amount '${amountBN.toString()}' is not a number`);
|
||||
if (amountBN.lte(0)) throw new Error(`Amount '${amountBN.toString()}' should be greater than 0`);
|
||||
|
||||
const sourceChainUnit = orion.getUnit(sourceChain);
|
||||
const targetChainUnit = orion.getUnit(targetChain);
|
||||
const sourceChainUnit = unitsArray.find(({ chainId }) => chainId === sourceChain);
|
||||
const targetChainUnit = unitsArray.find(({ chainId }) => chainId === targetChain);
|
||||
|
||||
if (sourceChainUnit === undefined) throw new Error(`Source chain '${sourceChain}' not found`);
|
||||
if (targetChainUnit === undefined) throw new Error(`Target chain '${targetChain}' not found`);
|
||||
|
||||
const {
|
||||
blockchainService: sourceBlockchainService,
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
import type { BigNumber } from 'bignumber.js';
|
||||
import type { ethers } from 'ethers';
|
||||
import { merge } from 'merge-anything';
|
||||
import { chains, envs } from '../config/index.js';
|
||||
import type { networkCodes } from '../constants/index.js';
|
||||
import Unit from '../Unit/index.js';
|
||||
import { ReferralSystem } from '../services/ReferralSystem/index.js';
|
||||
import type { SupportedChainId, DeepPartial, VerboseUnitConfig, KnownEnv } from '../types.js';
|
||||
import type { SupportedChainId, DeepPartial, VerboseUnitConfig, KnownEnv, EnvConfig, AggregatedAssets } from '../types.js';
|
||||
import { isValidChainId } from '../utils/index.js';
|
||||
import swap from './bridge/swap.js';
|
||||
import getHistory from './bridge/getHistory.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
|
||||
type EnvConfig = {
|
||||
analyticsAPI: string
|
||||
referralAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
}
|
||||
type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>;
|
||||
import Bridge from './bridge/index.js';
|
||||
|
||||
export default class Orion {
|
||||
public readonly env?: string;
|
||||
@@ -39,6 +15,8 @@ export default class Orion {
|
||||
|
||||
public readonly referralSystem: ReferralSystem;
|
||||
|
||||
public readonly bridge: Bridge;
|
||||
|
||||
// TODO: get tradable assets (aggregated)
|
||||
|
||||
// TODO: get tradable pairs (aggregated)
|
||||
@@ -117,6 +95,10 @@ export default class Orion {
|
||||
[chainId]: unit,
|
||||
}
|
||||
}, {});
|
||||
|
||||
this.bridge = new Bridge(
|
||||
this.unitsArray,
|
||||
);
|
||||
}
|
||||
|
||||
get unitsArray() {
|
||||
@@ -211,28 +193,4 @@ export default class Orion {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bridge = {
|
||||
getHistory: (address: string, limit = 1000) => getHistory(this.unitsArray, address, limit),
|
||||
swap: (
|
||||
assetName: string,
|
||||
amount: BigNumber.Value,
|
||||
sourceChain: SupportedChainId,
|
||||
targetChain: SupportedChainId,
|
||||
signer: ethers.Signer,
|
||||
options: {
|
||||
autoApprove?: boolean
|
||||
logger?: (message: string) => void
|
||||
withdrawToWallet?: boolean
|
||||
}
|
||||
) => swap({
|
||||
amount,
|
||||
assetName,
|
||||
sourceChain,
|
||||
targetChain,
|
||||
signer,
|
||||
orion: this,
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,4 @@
|
||||
import type exchanges from './exchanges.js';
|
||||
|
||||
const mapping: Record<
|
||||
typeof exchanges[number],
|
||||
string
|
||||
> = {
|
||||
const mapping: Record<string, string> = {
|
||||
// CEXes
|
||||
ASCENDEX: 'AscendEx',
|
||||
OKX: 'OKX',
|
||||
@@ -28,6 +23,6 @@ const mapping: Record<
|
||||
OKXSWAP: 'OKXSwap',
|
||||
CURVE: 'Curve',
|
||||
CURVE_FACTORY: 'Curve Factory',
|
||||
} as const;
|
||||
};
|
||||
|
||||
export default mapping;
|
||||
|
||||
@@ -8,7 +8,7 @@ import errorSchema from './schemas/errorSchema.js';
|
||||
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema.js';
|
||||
import { AggregatorWS } from './ws/index.js';
|
||||
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema.js';
|
||||
import type { BasicAuthCredentials, Exchange, SignedCancelOrderRequest, SignedOrder } from '../../types.js';
|
||||
import type { BasicAuthCredentials, SignedCancelOrderRequest, SignedOrder } from '../../types.js';
|
||||
import {
|
||||
pairConfigSchema, aggregatedOrderbookSchema,
|
||||
exchangeOrderbookSchema, poolReservesSchema,
|
||||
@@ -18,7 +18,6 @@ import toUpperCase from '../../utils/toUpperCase.js';
|
||||
import httpToWS from '../../utils/httpToWS.js';
|
||||
import { ethers } from 'ethers';
|
||||
import orderSchema from './schemas/orderSchema.js';
|
||||
import { exchanges } from '../../constants/index.js';
|
||||
import { fetchWithValidation } from 'simple-typed-fetch';
|
||||
|
||||
class Aggregator {
|
||||
@@ -119,12 +118,12 @@ class Aggregator {
|
||||
|
||||
getAvailableExchanges = () => fetchWithValidation(
|
||||
`${this.apiUrl}/api/v1/exchange/list`,
|
||||
z.enum(exchanges).array(),
|
||||
z.string().array(),
|
||||
);
|
||||
|
||||
getExchangeOrderbook = (
|
||||
pair: string,
|
||||
exchange: Exchange,
|
||||
exchange: string,
|
||||
depth = 20,
|
||||
filterByBrokerBalances: boolean | null = null,
|
||||
) => {
|
||||
@@ -156,7 +155,7 @@ class Aggregator {
|
||||
|
||||
getPoolReserves = (
|
||||
pair: string,
|
||||
exchange: Exchange,
|
||||
exchange: string,
|
||||
) => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/pools/reserves/${exchange}/${pair}`);
|
||||
return fetchWithValidation(
|
||||
@@ -255,7 +254,7 @@ class Aggregator {
|
||||
assetOut: string,
|
||||
amount: string,
|
||||
instantSettlement?: boolean,
|
||||
exchanges?: Exchange[] | 'cex' | 'pools',
|
||||
exchanges?: string[] | 'cex' | 'pools',
|
||||
) => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/swap`);
|
||||
url.searchParams.append('assetIn', assetIn);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import { exchanges } from '../../../constants/index.js';
|
||||
|
||||
const orderbookElementSchema = z.object({
|
||||
price: z.number(),
|
||||
@@ -12,7 +11,7 @@ const orderbookElementSchema = z.object({
|
||||
|
||||
const aggregatedOrderbookElementSchema = orderbookElementSchema
|
||||
.extend({
|
||||
exchanges: z.enum(exchanges).array(),
|
||||
exchanges: z.string().array(),
|
||||
});
|
||||
|
||||
export const aggregatedOrderbookSchema = z.object({
|
||||
|
||||
@@ -86,7 +86,7 @@ const subOrderSchema = baseOrderSchema.extend({
|
||||
parentOrderId: z.string().refine(ethers.utils.isHexString, (value) => ({
|
||||
message: `subOrder.parentOrderId must be a hex string, got ${value}`,
|
||||
})),
|
||||
exchange: z.enum(exchanges),
|
||||
exchange: z.string(),
|
||||
brokerAddress: brokerAddressSchema,
|
||||
tradesInfo: z.record(
|
||||
z.string().uuid(),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import { exchanges } from '../../../constants/index.js';
|
||||
|
||||
const orderInfoSchema = z.object({
|
||||
assetPair: z.string().toUpperCase(),
|
||||
@@ -18,13 +17,13 @@ const swapInfoBase = z.object({
|
||||
// isThroughPoolOptimal: z.boolean(), // deprecated
|
||||
executionInfo: z.string(),
|
||||
orderInfo: orderInfoSchema,
|
||||
exchanges: z.array(z.enum(exchanges)),
|
||||
exchanges: z.array(z.string()),
|
||||
price: z.number().nullable(), // spending asset price
|
||||
minAmountOut: z.number(),
|
||||
minAmountIn: z.number(),
|
||||
marketPrice: z.number().nullable(), // spending asset market price
|
||||
alternatives: z.object({ // execution alternatives
|
||||
exchanges: z.array(z.enum(exchanges)),
|
||||
exchanges: z.array(z.string()),
|
||||
path: z.object({
|
||||
units: z.object({
|
||||
assetPair: z.string().toUpperCase(),
|
||||
@@ -39,7 +38,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({
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import UnsubscriptionType from './UnsubscriptionType.js';
|
||||
import type {
|
||||
SwapInfoBase, AssetPairUpdate, OrderbookItem,
|
||||
Balance, Exchange, SwapInfo, Json, BasicAuthCredentials,
|
||||
Balance, SwapInfo, Json, BasicAuthCredentials,
|
||||
} from '../../../types.js';
|
||||
import unsubscriptionDoneSchema from './schemas/unsubscriptionDoneSchema.js';
|
||||
import assetPairConfigSchema from './schemas/assetPairConfigSchema.js';
|
||||
@@ -42,7 +42,7 @@ type SwapInfoSubscriptionPayload = {
|
||||
i: string // asset in
|
||||
o: string // asset out
|
||||
a: number // amount IN/OUT
|
||||
es?: Exchange[] | 'cex' | 'pools' // exchange list of all cex or all pools (ORION_POOL, UNISWAP, PANCAKESWAP etc)
|
||||
es?: string[] | 'cex' | 'pools' // exchange list of all cex or all pools (ORION_POOL, UNISWAP, PANCAKESWAP etc)
|
||||
e?: boolean // is amount IN? Value `false` means a = amount OUT, `true` if omitted
|
||||
is?: boolean // instant settlement
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import { exchanges } from '../../../../constants/index.js';
|
||||
import orderStatuses from '../../../../constants/orderStatuses.js';
|
||||
import executionTypes from '../../../../constants/executionTypes.js';
|
||||
import subOrderStatuses from '../../../../constants/subOrderStatuses.js';
|
||||
@@ -23,7 +22,7 @@ const subOrderSchema = z.object({
|
||||
a: z.number(), // amount
|
||||
A: z.number(), // settled amount
|
||||
p: z.number(), // avg weighed settlement price
|
||||
e: z.enum(exchanges), // exchange
|
||||
e: z.string(), // exchange
|
||||
b: z.string(), // broker address
|
||||
S: z.enum(subOrderStatuses), // status
|
||||
o: z.boolean(), // internal only
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import exchanges from '../../../../constants/exchanges.js';
|
||||
import MessageType from '../MessageType.js';
|
||||
import baseMessageSchema from './baseMessageSchema.js';
|
||||
|
||||
@@ -7,7 +6,7 @@ export const orderBookItemSchema = z.tuple([
|
||||
z.string(), // price
|
||||
z.string(), // size
|
||||
z.array(
|
||||
z.enum(exchanges),
|
||||
z.string(),
|
||||
), // exchanges
|
||||
z.array(z.tuple([
|
||||
z.enum(['SELL', 'BUY']), // side
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
import exchanges from '../../../../constants/exchanges.js';
|
||||
import MessageType from '../MessageType.js';
|
||||
import baseMessageSchema from './baseMessageSchema.js';
|
||||
|
||||
const alternativeSchema = z.object({ // execution alternatives
|
||||
e: z.enum(exchanges).array(), // exchanges
|
||||
e: z.string().array(), // exchanges
|
||||
ps: z.string().array(), // path
|
||||
mo: z.number().optional(), // market amount out
|
||||
mi: z.number().optional(), // market amount in
|
||||
@@ -23,7 +22,7 @@ const swapInfoSchemaBase = baseMessageSchema.extend({
|
||||
mao: z.number(), // min amount out
|
||||
ps: z.string().array(), // path
|
||||
po: z.boolean(), // is swap through pool optimal
|
||||
e: z.enum(exchanges).array().optional(), // Exchanges
|
||||
e: z.string().array().optional(), // Exchanges
|
||||
p: z.number().optional(), // price
|
||||
mp: z.number().optional(), // market price
|
||||
oi: z.object({ // info about order equivalent to this swap
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fetchWithValidation } from 'simple-typed-fetch';
|
||||
import type { BasicAuthCredentials, Exchange } from '../../types.js';
|
||||
import type { BasicAuthCredentials } from '../../types.js';
|
||||
import { allTickersSchema, statisticsOverviewSchema, topPairsStatisticsSchema } from './schemas/index.js';
|
||||
import candlesSchema from './schemas/candlesSchema.js';
|
||||
import { PriceFeedWS } from './ws/index.js';
|
||||
@@ -56,7 +56,7 @@ class PriceFeed {
|
||||
);
|
||||
};
|
||||
|
||||
getStatisticsOverview = (exchange: Exchange | 'ALL' = 'ALL') => {
|
||||
getStatisticsOverview = (exchange: string | 'ALL' = 'ALL') => {
|
||||
const url = new URL(`${this.statisticsUrl}/overview`);
|
||||
url.searchParams.append('exchange', exchange);
|
||||
|
||||
@@ -67,7 +67,7 @@ class PriceFeed {
|
||||
);
|
||||
}
|
||||
|
||||
getTopPairStatistics = (exchange: Exchange | 'ALL' = 'ALL') => {
|
||||
getTopPairStatistics = (exchange: string | 'ALL' = 'ALL') => {
|
||||
const url = new URL(`${this.statisticsUrl}/top-pairs`);
|
||||
url.searchParams.append('exchange', exchange);
|
||||
|
||||
|
||||
75
src/types.ts
75
src/types.ts
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
||||
import type { BigNumber } from 'bignumber.js';
|
||||
import type exchanges from './constants/exchanges.js';
|
||||
import type subOrderStatuses from './constants/subOrderStatuses.js';
|
||||
import type positionStatuses from './constants/positionStatuses.js';
|
||||
import type { knownEnvs } from './config/schemas/index.js';
|
||||
@@ -143,12 +142,12 @@ export type BalanceIssue = {
|
||||
readonly fixes?: Fix[]
|
||||
}
|
||||
|
||||
export type Exchange = typeof exchanges[number];
|
||||
// export type Exchange = typeof exchanges[number];
|
||||
|
||||
export type OrderbookItem = {
|
||||
price: string
|
||||
amount: string
|
||||
exchanges: Exchange[]
|
||||
exchanges: string[]
|
||||
vob: Array<{
|
||||
side: 'BUY' | 'SELL'
|
||||
pairName: string
|
||||
@@ -156,7 +155,7 @@ export type OrderbookItem = {
|
||||
}
|
||||
|
||||
export type SwapInfoAlternative = {
|
||||
exchanges: Exchange[]
|
||||
exchanges: string[]
|
||||
path: string[]
|
||||
marketAmountOut?: number | undefined
|
||||
marketAmountIn?: number | undefined
|
||||
@@ -175,7 +174,7 @@ export type SwapInfoBase = {
|
||||
minAmountOut: number
|
||||
|
||||
path: string[]
|
||||
exchanges?: Exchange[] | undefined
|
||||
exchanges?: string[] | undefined
|
||||
poolOptimal: boolean
|
||||
|
||||
price?: number | undefined
|
||||
@@ -251,3 +250,69 @@ export type VerboseUnitConfig = {
|
||||
export type KnownEnv = typeof knownEnvs[number];
|
||||
|
||||
export type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
|
||||
|
||||
export type EnvConfig = {
|
||||
analyticsAPI: string
|
||||
referralAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
}
|
||||
export type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
export type RedeemOrder = {
|
||||
sender: string
|
||||
receiver: string
|
||||
asset: string
|
||||
amount: number
|
||||
expiration: number
|
||||
secretHash: string
|
||||
signature: string
|
||||
claimReceiver: string
|
||||
}
|
||||
|
||||
export type AtomicSwap = {
|
||||
secret: string
|
||||
secretHash: string
|
||||
|
||||
walletAddress: string
|
||||
env?: string | undefined
|
||||
|
||||
sourceNetwork?: SupportedChainId
|
||||
targetNetwork?: SupportedChainId
|
||||
|
||||
amount?: string
|
||||
asset?: string
|
||||
|
||||
creationDate?: number
|
||||
expiration?: number
|
||||
|
||||
lockTransactionHash?: string
|
||||
redeemTransactionHash?: string
|
||||
refundTransactionHash?: string
|
||||
liquidityMigrationTxHash?: string
|
||||
|
||||
redeemOrder?: RedeemOrder
|
||||
}
|
||||
|
||||
export type ExternalStorage = {
|
||||
bridge: {
|
||||
getAtomicSwaps: () => AtomicSwap[]
|
||||
setAtomicSwaps: (atomics: AtomicSwap[]) => void
|
||||
addAtomicSwap: (atomic: AtomicSwap) => void
|
||||
updateAtomicSwap: (secretHash: string, atomic: Partial<AtomicSwap>) => void
|
||||
removeAtomicSwaps: (secretHashes: string[]) => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
baseAssetName: string,
|
||||
baseCurrencyName: string,
|
||||
feeAssetName: string,
|
||||
prices: Partial<Record<string, string>>,
|
||||
) => {
|
||||
const feeAssetPrice = prices[feeAssetName];
|
||||
if (feeAssetPrice === undefined) throw Error(`Fee asset price not found. Available prices: ${Object.keys(prices).join(', ')}`);
|
||||
const baseAssetPrice = prices[baseAssetName];
|
||||
if (baseAssetPrice === undefined) throw Error(`Base asset price not found. Available prices: ${Object.keys(prices).join(', ')}`);
|
||||
const baseCurrencyPrice = prices[baseCurrencyName]; // 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,
|
||||
baseAssetName,
|
||||
feeAssetName,
|
||||
feePercent,
|
||||
prices,
|
||||
);
|
||||
const networkFeeInFeeAsset = calculateNetworkFeeInFeeAsset(
|
||||
gasPriceGwei,
|
||||
FILL_ORDERS_GAS_LIMIT,
|
||||
baseCurrencyPriceInServiceToken,
|
||||
feeAssetPriceInServiceToken,
|
||||
baseCurrencyName,
|
||||
feeAssetName,
|
||||
prices,
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -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,
|
||||
baseCurrencyName: string,
|
||||
feeAssetName: string,
|
||||
prices: Partial<Record<string, string>>
|
||||
) => {
|
||||
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,
|
||||
baseCurrencyName, // from
|
||||
feeAssetName, // to
|
||||
prices
|
||||
);
|
||||
};
|
||||
|
||||
export default calculateNetworkFeeInFeeAsset;
|
||||
|
||||
@@ -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,
|
||||
baseAssetName: string,
|
||||
feeAssetName: string,
|
||||
feePercent: BigNumber.Value,
|
||||
prices: Partial<Record<string, string>>
|
||||
) {
|
||||
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,
|
||||
baseAssetName,
|
||||
feeAssetName,
|
||||
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,
|
||||
assetInName: string,
|
||||
assetOutName: string,
|
||||
prices: Partial<Record<string, string>> // quoted in quoteAsset. [name]: priceQuotedInQuoteAsset
|
||||
) {
|
||||
const assetInPrice = prices[assetInName];
|
||||
if (assetInPrice === undefined) throw Error(`Price conversion: AssetIn (${assetInName}) price is undefined. Available prices: ${JSON.stringify(prices)}`);
|
||||
|
||||
const assetOutPrice = prices[assetOutName];
|
||||
if (assetOutPrice === undefined) throw Error(`Price conversion: AssetOut (${assetOutName}) price is undefined. Available prices: ${JSON.stringify(prices)}`);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export { default as getNativeCryptocurrencyName } from './getNativeCryptocurrenc
|
||||
export { default as laconicParse } from './laconic-parse.js';
|
||||
export { default as isValidChainId } from './isValidChainId.js';
|
||||
export { default as isKnownEnv } from './isKnownEnv.js';
|
||||
export { default as removeFieldsFromObject } from './removeFieldsFromObject.js';
|
||||
// export { default as HttpError } from './httpError';
|
||||
|
||||
export * from './typeHelpers.js';
|
||||
|
||||
16
src/utils/removeFieldsFromObject.ts
Normal file
16
src/utils/removeFieldsFromObject.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
const removeFieldsFromObject = <
|
||||
T extends Record<string, unknown>,
|
||||
K extends keyof T
|
||||
>(
|
||||
obj: T,
|
||||
fields: K[]
|
||||
): Omit<T, K> => {
|
||||
const result = { ...obj };
|
||||
for (const field of fields) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete result[field];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export default removeFieldsFromObject;
|
||||
Reference in New Issue
Block a user