This commit is contained in:
Aleksandr Kraiz
2023-02-13 17:08:58 +04:00
parent 3e248b4c8d
commit 1fdd65f4a0
42 changed files with 618 additions and 588 deletions

View File

@@ -6,11 +6,13 @@ module.exports = {
node: true,
},
extends: [
'standard',
'standard-with-typescript',
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'plugin:import/recommended',
'plugin:import/typescript'
],
parser: '@typescript-eslint/parser',
@@ -26,6 +28,21 @@ module.exports = {
'@typescript-eslint',
],
rules: {
"@typescript-eslint/indent": [
"error",
2,
{
"SwitchCase": 1,
"ignoredNodes": [
"TSTypeParameterInstantiation"
]
}
],
"@typescript-eslint/promise-function-async": 0,
"import/no-cycle": "error",
"@typescript-eslint/space-before-function-paren": 0,
"@typescript-eslint/comma-dangle": 0,
"@typescript-eslint/semi": 0,
"comma-dangle": 0,
"semi": 0,
"space-before-function-paren": 0,

View File

@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm test
# npm test

View File

@@ -11,5 +11,6 @@
"./README.md": [
"# Orion Protocol SDK"
]
}
},
"typescript.tsdk": "node_modules/typescript/lib"
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@orionprotocol/sdk",
"version": "0.17.5-rc.0",
"version": "0.17.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@orionprotocol/sdk",
"version": "0.17.5-rc.0",
"version": "0.17.5",
"license": "ISC",
"dependencies": {
"@ethersproject/abstract-signer": "^5.7.0",

View File

@@ -12,13 +12,13 @@ import arrayEquals from './utils/arrayEquals';
export default class BalanceGuard {
private readonly balances: Partial<
Record<
string,
Record<
'exchange' | 'wallet',
BigNumber>
>
>;
Record<
string,
Record<
'exchange' | 'wallet',
BigNumber>
>
>;
public readonly requirements: BalanceRequirement[] = [];
@@ -51,7 +51,7 @@ export default class BalanceGuard {
// Used for case feeAsset === assetOut
setExtraBalance(assetName: string, amount: BigNumber.Value, source: Source) {
const assetBalance = this.balances[assetName];
if (!assetBalance) throw Error(`Can't set extra balance. Asset ${assetName} not found`);
if (assetBalance == null) throw Error(`Can't set extra balance. Asset ${assetName} not found`);
assetBalance[source] = assetBalance[source].plus(amount);
}
@@ -87,7 +87,7 @@ export default class BalanceGuard {
item.spenderAddress === curr.spenderAddress,
);
if (aggregatedBalanceRequirement) {
if (aggregatedBalanceRequirement != null) {
aggregatedBalanceRequirement.items = {
...aggregatedBalanceRequirement.items,
[curr.reason]: curr.amount,
@@ -109,7 +109,7 @@ export default class BalanceGuard {
}, []);
}
private fixAllAutofixableBalanceIssues = async (
private readonly fixAllAutofixableBalanceIssues = async (
balanceIssues: BalanceIssue[],
) => {
const fixBalanceIssue = async (issue: BalanceIssue) => {
@@ -146,8 +146,8 @@ export default class BalanceGuard {
};
await issue.fixes?.reduce(async (promise, item) => {
await promise;
if (item.type === 'byApprove') return approve(item);
return promise;
if (item.type === 'byApprove') { await approve(item); return; }
await promise;
}, Promise.resolve());
};
@@ -155,7 +155,7 @@ export default class BalanceGuard {
await autofixableBalanceIssues.reduce(async (promise, item) => {
await promise;
return fixBalanceIssue(item);
await fixBalanceIssue(item);
}, Promise.resolve());
return balanceIssues.filter((item) => !autofixableBalanceIssues.includes(item));
@@ -194,20 +194,20 @@ export default class BalanceGuard {
exchangeOnlyAggregatedRequirements.forEach(({ asset, items }) => {
const remainingBalance = remainingBalances[asset.name];
if (!remainingBalance) throw new Error(`No ${asset.name} balance`);
if (remainingBalance == null) throw new Error(`No ${asset.name} balance`);
const itemsAmountSum = Object.values(items)
.reduce<BigNumber>((p, c) => (c ? p.plus(c) : p), new BigNumber(0));
.reduce<BigNumber>((p, c) => (c !== undefined ? p.plus(c) : p), new BigNumber(0));
remainingBalance.exchange = remainingBalance.exchange.minus(itemsAmountSum);
if (remainingBalance.exchange.lt(0)) {
const lackAmount = remainingBalance.exchange.abs();
const exchangeBalance = this.balances?.[asset.name]?.exchange;
const exchangeBalance = this.balances[asset.name]?.exchange;
balanceIssues.push({
asset,
sources: ['exchange'],
message: `Not enough ${asset.name} on exchange balance. ` +
`Needed: ${itemsAmountSum.toString()}, available: ${(exchangeBalance ?? '[UNDEFINED]')?.toString()}. ` +
`Needed: ${itemsAmountSum.toString()}, available: ${(exchangeBalance ?? '[UNDEFINED]').toString()}. ` +
`You need to deposit at least ${lackAmount.toString()} ${asset.name} into exchange contract`,
});
}
@@ -221,9 +221,9 @@ export default class BalanceGuard {
await Promise.all(exchangePlusWalletAggregatedRequirements
.map(async ({ asset, spenderAddress, items }) => {
const remainingBalance = remainingBalances[asset.name];
if (!remainingBalance) throw new Error(`No ${asset.name} balance`);
if (remainingBalance == null) throw new Error(`No ${asset.name} balance`);
const itemsAmountSum = Object.values(items)
.reduce<BigNumber>((p, c) => (c ? p.plus(c) : p), new BigNumber(0));
.reduce<BigNumber>((p, c) => (c !== undefined ? p.plus(c) : p), new BigNumber(0));
remainingBalance.exchange = remainingBalance.exchange.minus(itemsAmountSum);
if (remainingBalance.exchange.lt(0)) {
@@ -233,7 +233,7 @@ export default class BalanceGuard {
if (asset.address === ethers.constants.AddressZero) {
denormalizedAllowance = remainingBalance.wallet;
} else {
if (!spenderAddress) throw new Error(`Spender address is required for ${asset.name}`);
if (spenderAddress === undefined) throw new Error(`Spender address is required for ${asset.name}`);
const tokenContract = ERC20__factory.connect(asset.address, this.provider);
const tokenDecimals = await tokenContract.decimals();
const tokenAllowance = await tokenContract.allowance(walletAddress, spenderAddress);
@@ -257,17 +257,17 @@ export default class BalanceGuard {
const approveIsHelpful = approveAvailable.gte(lackAmount);
const targetApprove = approvedWalletBalance.plus(lackAmount);
const exchangeBalance = this.balances?.[asset.name]?.exchange;
const exchangeBalance = this.balances[asset.name]?.exchange;
const available = exchangeBalance?.plus(approvedWalletBalance);
const issueMessage = `Not enough ${asset.name} on exchange + wallet balance. ` +
`Needed: ${itemsAmountSum.toString()}, available: ${(available ?? '[UNDEFINED]')?.toString()} ` +
`(exchange: ${(exchangeBalance ?? '[UNKNOWN]')?.toString()}, available (approved): ${approvedWalletBalance.toString()}).` +
`Needed: ${itemsAmountSum.toString()}, available: ${(available ?? '[UNDEFINED]').toString()} ` +
`(exchange: ${(exchangeBalance ?? '[UNKNOWN]').toString()}, available (approved): ${approvedWalletBalance.toString()}).` +
` ${approveIsHelpful
? `You need approve at least ${lackAmount.toString()} ${asset.name}`
: 'Approve is not helpful'}`;
if (approveIsHelpful) {
if (!spenderAddress) throw new Error(`Spender address is required for ${asset.name}`);
if (spenderAddress === undefined) throw new Error(`Spender address is required for ${asset.name}`);
const resetRequired = await this.checkResetRequired(
asset.address,
spenderAddress,
@@ -292,10 +292,10 @@ export default class BalanceGuard {
fixes: [
...resetRequired
? [{
type: 'byApprove' as const,
targetAmount: 0,
spenderAddress,
}]
type: 'byApprove' as const,
targetAmount: 0,
spenderAddress,
}]
: [],
{
type: 'byApprove',
@@ -322,15 +322,15 @@ export default class BalanceGuard {
await Promise.all(walletTokensAggregatedRequirements
.map(async ({ asset, spenderAddress, items }) => {
const remainingBalance = remainingBalances[asset.name];
if (!remainingBalance) throw new Error(`No ${asset.name} balance`);
if (remainingBalance == null) throw new Error(`No ${asset.name} balance`);
const itemsAmountSum = Object.values(items)
.reduce<BigNumber>((p, c) => (c ? p.plus(c) : p), new BigNumber(0));
.reduce<BigNumber>((p, c) => (c !== undefined ? p.plus(c) : p), new BigNumber(0));
let denormalizedAllowance: BigNumber;
if (asset.address === ethers.constants.AddressZero) {
denormalizedAllowance = remainingBalance.wallet;
} else {
if (!spenderAddress) throw new Error(`Spender address is required for ${asset.name}`);
if (spenderAddress === undefined) throw new Error(`Spender address is required for ${asset.name}`);
const tokenContract = ERC20__factory.connect(asset.address, this.provider);
const tokenDecimals = await tokenContract.decimals();
const tokenAllowance = await tokenContract.allowance(walletAddress, spenderAddress);
@@ -358,7 +358,7 @@ export default class BalanceGuard {
? `You need approve at least ${lackAmount.toString()} ${asset.name}`
: 'Approve is not helpful'}`;
if (approveIsHelpful) {
if (!spenderAddress) throw new Error(`Spender address is required for ${asset.name}`);
if (spenderAddress === undefined) throw new Error(`Spender address is required for ${asset.name}`);
const resetRequired = await this.checkResetRequired(
asset.address,
spenderAddress,
@@ -383,10 +383,10 @@ export default class BalanceGuard {
fixes: [
...resetRequired
? [{
type: 'byApprove' as const,
targetAmount: 0,
spenderAddress,
}]
type: 'byApprove' as const,
targetAmount: 0,
spenderAddress,
}]
: [],
{
type: 'byApprove',
@@ -411,10 +411,10 @@ export default class BalanceGuard {
walletNativeAggregatedRequirements.forEach(({ asset, items }) => {
const remainingBalance = remainingBalances[asset.name];
if (!remainingBalance) throw new Error(`No ${asset.name} balance`);
if (remainingBalance == null) throw new Error(`No ${asset.name} balance`);
const itemsAmountSum = Object.values({ ...items, ...requiredApproves.items })
.reduce<BigNumber>((p, c) => (c ? p.plus(c) : p), new BigNumber(0));
.reduce<BigNumber>((p, c) => (c !== undefined ? p.plus(c) : p), new BigNumber(0));
remainingBalance.wallet = remainingBalance.wallet.minus(itemsAmountSum);
if (remainingBalance.wallet.lt(0)) {
@@ -428,7 +428,7 @@ export default class BalanceGuard {
}
});
if (fixAutofixable) {
if (fixAutofixable !== undefined && fixAutofixable) {
const unfixed = await this.fixAllAutofixableBalanceIssues(balanceIssues);
if (unfixed.length > 0) throw new Error(`Balance issues: ${unfixed.map((issue, i) => `${i + 1}. ${issue.message}`).join('\n')}`);
} else if (balanceIssues.length > 0) {

View File

@@ -1,21 +1,21 @@
import { merge } from 'merge-anything';
import { chains, envs } from '../config';
import { networkCodes } from '../constants';
import { type networkCodes } from '../constants';
import OrionUnit from '../OrionUnit';
import OrionAnalytics from '../services/OrionAnalytics';
import { ReferralSystem } from '../services/ReferralSystem';
import { DeepPartial, SupportedChainId, VerboseOrionUnitConfig } from '../types';
import { type DeepPartial, type SupportedChainId, type VerboseOrionUnitConfig } from '../types';
import { isValidChainId } from '../utils';
type EnvConfig = {
analyticsAPI: string;
referralAPI: string;
interface EnvConfig {
analyticsAPI: string
referralAPI: string
networks: Partial<
Record<
SupportedChainId,
VerboseOrionUnitConfig
>
>;
>
}
type KnownEnv = 'testing' | 'staging' | 'production';
@@ -29,14 +29,6 @@ export default class Orion {
public readonly referralSystem: ReferralSystem;
constructor();
constructor(
env: KnownEnv,
overrides?: DeepPartial<EnvConfig>
);
constructor(config: EnvConfig);
// TODO: get tradable assets (aggregated)
// TODO: get tradable pairs (aggregated)
@@ -50,7 +42,9 @@ export default class Orion {
let config: EnvConfig;
if (typeof envOrConfig === 'string') {
const envConfig = envs[envOrConfig];
if (!envConfig) throw new Error(`Invalid environment: ${envOrConfig}. Available environments: ${Object.keys(envs).join(', ')}`);
if (envConfig === undefined) {
throw new Error(`Invalid environment: ${envOrConfig}. Available environments: ${Object.keys(envs).join(', ')}`);
}
this.env = envOrConfig;
config = {
analyticsAPI: envConfig.analyticsAPI,
@@ -58,13 +52,15 @@ export default class Orion {
networks: Object.entries(envConfig.networks).map(([chainId, networkConfig]) => {
if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`);
const chainConfig = chains[chainId];
if (!chainConfig) throw new Error(`Chain config not found: ${chainId}. Available chains: ${Object.keys(chains).join(', ')}`);
if (chainConfig === undefined) {
throw new Error(`Chain config not found: ${chainId}. Available chains: ${Object.keys(chains).join(', ')}`);
}
return {
env: envOrConfig,
chainId,
api: networkConfig.api,
nodeJsonRpc: networkConfig?.rpc ?? chainConfig.rpc,
nodeJsonRpc: networkConfig.rpc ?? chainConfig.rpc,
services: {
orionBlockchain: {
http: networkConfig.api + networkConfig.services.blockchain.http,
@@ -85,7 +81,8 @@ export default class Orion {
}, {}),
};
if (overrides) {
if (overrides !== undefined) {
// Recursive merge of config and overrides. Ignore undefined values.
config = merge(config, overrides);
}
} else {
@@ -99,7 +96,7 @@ export default class Orion {
.reduce<Partial<Record<SupportedChainId, OrionUnit>>>((acc, [chainId, networkConfig]) => {
if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`);
const chainConfig = chains[chainId];
if (!chainConfig) throw new Error(`Invalid chainId: ${chainId}`);
if (chainConfig == null) throw new Error(`Invalid chainId: ${chainId}`);
const orionUnit = new OrionUnit({
// env: networkConfig.env,
@@ -119,20 +116,16 @@ export default class Orion {
return Object.entries(this.units).map(([, unit]) => unit);
}
getUnit(chainId: SupportedChainId): OrionUnit;
getUnit(networkCode: typeof networkCodes[number]): OrionUnit;
getUnit(networkCodeOrChainId: string): OrionUnit {
getUnit(networkCodeOrChainId: typeof networkCodes[number] | SupportedChainId): OrionUnit {
let unit: OrionUnit | undefined;
if (isValidChainId(networkCodeOrChainId)) {
unit = this.units[networkCodeOrChainId];
} else {
unit = this.unitsArray.find((u) => u.networkCode === networkCodeOrChainId);
}
if (!unit) {
if (unit == null) {
throw new Error(
`Invalid network code: ${networkCodeOrChainId}. ` +
`Invalid network code: ${networkCodeOrChainId}. ` +
`Available network codes: ${this.unitsArray.map((u) => u.networkCode).join(', ')}`);
}
return unit;

View File

@@ -4,7 +4,7 @@ import { ethers } from 'ethers';
import { Exchange__factory } from '@orionprotocol/contracts';
import getBalances from '../../utils/getBalances';
import BalanceGuard from '../../BalanceGuard';
import OrionUnit from '..';
import type OrionUnit from '..';
import { utils } from '../..';
import {
DEPOSIT_ERC20_GAS_LIMIT, DEPOSIT_ETH_GAS_LIMIT, INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION,
@@ -13,11 +13,11 @@ import { normalizeNumber } from '../../utils';
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
import simpleFetch from '../../simpleFetch';
export type DepositParams = {
asset: string,
amount: BigNumber.Value,
signer: ethers.Signer,
orionUnit: OrionUnit,
export interface DepositParams {
asset: string
amount: BigNumber.Value
signer: ethers.Signer
orionUnit: OrionUnit
}
export default async function deposit({
@@ -29,8 +29,8 @@ export default async function deposit({
if (asset === '') throw new Error('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`);
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 walletAddress = await signer.getAddress();
@@ -48,7 +48,7 @@ export default async function deposit({
const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)();
const assetAddress = assetToAddress[asset];
if (!assetAddress) throw new Error(`Asset '${asset}' not found`);
if (assetAddress === undefined) throw new Error(`Asset '${asset}' not found`);
const balances = await getBalances(
{
@@ -121,7 +121,7 @@ export default async function deposit({
const txResponse = await provider.sendTransaction(signedTx);
console.log(`Deposit tx sent: ${txResponse.hash}. Waiting for confirmation...`);
const txReceipt = await txResponse.wait();
if (txReceipt.status) {
if (txReceipt.status !== undefined) {
console.log('Deposit tx confirmed');
} else {
console.log('Deposit tx failed');

View File

@@ -2,23 +2,23 @@ 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 { type OrionAggregator } from '../../services/OrionAggregator';
import { type 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,
export interface GetSwapInfoParams {
type: 'exactSpend' | 'exactReceive'
assetIn: string
assetOut: string
amount: BigNumber.Value
feeAsset: string
orionBlockchain: OrionBlockchain
orionAggregator: OrionAggregator
options?: {
instantSettlement?: boolean,
poolOnly?: boolean,
instantSettlement?: boolean
poolOnly?: boolean
}
}
@@ -38,8 +38,8 @@ export default async function getSwapInfo({
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`);
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 {
assetToAddress,
@@ -53,17 +53,21 @@ export default async function getSwapInfo({
const gasPriceGwei = ethers.utils.formatUnits(gasPriceWei, 'gwei').toString();
const assetInAddress = assetToAddress[assetIn];
if (!assetInAddress) throw new Error(`Asset '${assetIn}' not found`);
if (assetInAddress === undefined) 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(', ')}`);
if (feeAssetAddress === undefined) {
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(),
amountBN.toString(),
options?.instantSettlement,
options?.poolOnly ? 'pools' : undefined,
options?.poolOnly !== undefined && options.poolOnly
? 'pools'
: undefined,
);
const { exchanges: swapExchanges } = swapInfo;
@@ -82,13 +86,12 @@ export default async function getSwapInfo({
// if (swapInfo.orderInfo === null) throw new Error(swapInfo.executionInfo);
let route: 'pool' | 'aggregator';
if (options?.poolOnly) {
if (options?.poolOnly !== undefined && options.poolOnly) {
route = 'pool';
} else if (
swapExchanges !== undefined &&
poolExchangesList.length > 0 &&
swapExchanges.length === 1 &&
firstSwapExchange &&
firstSwapExchange !== undefined &&
poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
) {
route = 'pool';
@@ -112,21 +115,21 @@ export default async function getSwapInfo({
};
}
if (swapInfo.orderInfo) {
if (swapInfo.orderInfo != null) {
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}`);
if (baseAssetAddress === undefined) 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 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) throw new Error('Base currency price in ORN not found');
if (baseCurrencyPriceInOrn === undefined) 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`);
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 {
orionFeeInFeeAsset,

View File

@@ -1,13 +1,13 @@
import OrionUnit from '..';
import deposit, { DepositParams } from './deposit';
import getSwapInfo, { GetSwapInfoParams } from './getSwapInfo';
import swapMarket, { SwapMarketParams } from './swapMarket';
import withdraw, { WithdrawParams } from './withdraw';
import type OrionUnit from '..';
import deposit, { type DepositParams } from './deposit';
import getSwapInfo, { type GetSwapInfoParams } from './getSwapInfo';
import swapMarket, { type Swap, type SwapMarketParams } from './swapMarket';
import withdraw, { type WithdrawParams } from './withdraw';
type PureSwapMarketParams= Omit<SwapMarketParams, 'orionUnit'>
type PureSwapMarketParams = Omit<SwapMarketParams, 'orionUnit'>
type PureDepositParams = Omit<DepositParams, 'orionUnit'>
type PureWithdrawParams = Omit<WithdrawParams, 'orionUnit'>
type PureGetSwapMarketInfoParams= Omit<GetSwapInfoParams, 'orionBlockchain' | 'orionAggregator'>
type PureGetSwapMarketInfoParams = Omit<GetSwapInfoParams, 'orionBlockchain' | 'orionAggregator'>
export default class Exchange {
private readonly orionUnit: OrionUnit;
@@ -16,7 +16,7 @@ export default class Exchange {
this.orionUnit = orionUnit;
}
public swapMarket(params: PureSwapMarketParams) {
public swapMarket(params: PureSwapMarketParams): Promise<Swap> {
return swapMarket({
...params,
orionUnit: this.orionUnit,

View File

@@ -5,43 +5,43 @@ import { Exchange__factory } from '@orionprotocol/contracts';
import getBalances from '../../utils/getBalances';
import BalanceGuard from '../../BalanceGuard';
import getAvailableSources from '../../utils/getAvailableFundsSources';
import OrionUnit from '..';
import type OrionUnit from '..';
import { crypt, utils } from '../..';
import { INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION, SWAP_THROUGH_ORION_POOL_GAS_LIMIT } from '../../constants';
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
import simpleFetch from '../../simpleFetch';
export type SwapMarketParams = {
type: 'exactSpend' | 'exactReceive',
assetIn: string,
assetOut: string,
amount: BigNumber.Value,
feeAsset: string,
slippagePercent: BigNumber.Value,
signer: ethers.Signer,
orionUnit: OrionUnit,
export interface SwapMarketParams {
type: 'exactSpend' | 'exactReceive'
assetIn: string
assetOut: string
amount: BigNumber.Value
feeAsset: string
slippagePercent: BigNumber.Value
signer: ethers.Signer
orionUnit: OrionUnit
options?: {
poolOnly?: boolean,
instantSettlement?: boolean,
logger?: (message: string) => void,
autoApprove?: boolean,
poolOnly?: boolean
instantSettlement?: boolean
logger?: (message: string) => void
autoApprove?: boolean
developer?: {
route?: 'aggregator' | 'pool',
route?: 'aggregator' | 'pool'
}
}
}
type AggregatorOrder = {
interface AggregatorOrder {
through: 'aggregator'
id: string,
id: string
}
type PoolSwap = {
interface PoolSwap {
through: 'orion_pool'
txHash: string,
txHash: string
}
type Swap = AggregatorOrder | PoolSwap;
export type Swap = AggregatorOrder | PoolSwap;
export default async function swapMarket({
type,
@@ -54,7 +54,7 @@ export default async function swapMarket({
orionUnit,
options,
}: SwapMarketParams): Promise<Swap> {
if (options?.developer) options?.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!');
if ((options?.developer) != null) options.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!');
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');
@@ -93,9 +93,9 @@ export default async function swapMarket({
const gasPriceGwei = ethers.utils.formatUnits(gasPriceWei, 'gwei').toString();
const assetInAddress = assetToAddress[assetIn];
if (!assetInAddress) throw new Error(`Asset '${assetIn}' not found`);
if (assetInAddress === undefined) 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(', ')}`);
if (feeAssetAddress === undefined) throw new Error(`Fee asset '${feeAsset}' not found. Available assets: ${Object.keys(feeAssets).join(', ')}`);
const balances = await getBalances(
{
@@ -124,16 +124,18 @@ export default async function swapMarket({
type,
assetIn,
assetOut,
amount.toString(),
amountBN.toString(),
options?.instantSettlement,
options?.poolOnly ? 'pools' : undefined,
options?.poolOnly !== undefined && options.poolOnly
? 'pools'
: undefined,
);
const { exchanges: swapExchanges } = swapInfo;
const [firstSwapExchange] = swapExchanges;
if (swapExchanges) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`);
if (swapExchanges.length > 0) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`);
if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) {
throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`);
@@ -150,8 +152,6 @@ export default async function swapMarket({
if (quoteAssetName === undefined) throw new Error('Quote asset name is undefined');
const pairConfig = await simpleFetch(orionAggregator.getPairConfig)(`${baseAssetName}-${quoteAssetName}`);
if (!pairConfig) throw new Error(`Pair config ${baseAssetName}-${quoteAssetName} not found`);
const qtyPrecisionBN = new BigNumber(pairConfig.qtyPrecision);
const qtyDecimalPlaces = amountBN.dp();
@@ -165,15 +165,14 @@ export default async function swapMarket({
if (options?.developer?.route !== undefined) {
route = options.developer.route;
options?.logger?.(`Swap is through ${route} (because route forced to ${route})`);
} else if (options?.poolOnly) {
options?.logger?.('Swap is through pool (because "poolOnly" option is true)');
options.logger?.(`Swap is through ${route} (because route forced to ${route})`);
} else if (options?.poolOnly !== undefined && options.poolOnly) {
options.logger?.('Swap is through pool (because "poolOnly" option is true)');
route = 'pool';
} else if (
swapExchanges !== undefined &&
poolExchangesList.length > 0 &&
swapExchanges.length === 1 &&
firstSwapExchange &&
firstSwapExchange !== undefined &&
poolExchangesList.some((poolExchange) => poolExchange === firstSwapExchange)
) {
options?.logger?.(`Swap is through pool [via ${firstSwapExchange}] (detected by "exchanges" field)`);
@@ -184,14 +183,14 @@ export default async function swapMarket({
if (route === 'pool') {
let factoryAddress: string | undefined;
if (factories && firstSwapExchange) {
factoryAddress = factories?.[firstSwapExchange];
if (factoryAddress) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`);
if ((factories != null) && firstSwapExchange !== undefined) {
factoryAddress = factories[firstSwapExchange];
if (factoryAddress !== undefined) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`);
}
const pathAddresses = swapInfo.path.map((name) => {
const assetAddress = assetToAddress?.[name];
if (!assetAddress) throw new Error(`No asset address for ${name}`);
const assetAddress = assetToAddress[name];
if (assetAddress === undefined) throw new Error(`No asset address for ${name}`);
return assetAddress;
});
@@ -229,7 +228,9 @@ export default async function swapMarket({
const unsignedSwapThroughOrionPoolTx = await exchangeContract.populateTransaction.swapThroughOrionPool(
amountSpendBlockchainParam,
amountReceiveBlockchainParam,
factoryAddress ? [factoryAddress, ...pathAddresses] : pathAddresses,
factoryAddress !== undefined
? [factoryAddress, ...pathAddresses]
: pathAddresses,
type === 'exactSpend',
);
@@ -241,7 +242,7 @@ export default async function swapMarket({
let value = new BigNumber(0);
const denormalizedAssetInExchangeBalance = balances[assetIn]?.exchange;
if (!denormalizedAssetInExchangeBalance) throw new Error(`Asset '${assetIn}' exchange balance is not found`);
if (denormalizedAssetInExchangeBalance == null) throw new Error(`Asset '${assetIn}' exchange balance is not found`);
if (assetIn === nativeCryptocurrency && amountSpendBN.gt(denormalizedAssetInExchangeBalance)) {
value = amountSpendBN.minus(denormalizedAssetInExchangeBalance);
}
@@ -305,9 +306,9 @@ export default async function swapMarket({
.toString();
const baseAssetAddress = assetToAddress[baseAssetName];
if (!baseAssetAddress) throw new Error(`No asset address for ${baseAssetName}`);
if (baseAssetAddress === undefined) throw new Error(`No asset address for ${baseAssetName}`);
const quoteAssetAddress = assetToAddress[quoteAssetName];
if (!quoteAssetAddress) throw new Error(`No asset address for ${quoteAssetName}`);
if (quoteAssetAddress === undefined) throw new Error(`No asset address for ${quoteAssetName}`);
const safePriceWithAppliedPrecision = new BigNumber(safePriceWithDeviation)
.decimalPlaces(
@@ -331,14 +332,14 @@ export default async function swapMarket({
});
// Fee calculation
const baseAssetPriceInOrn = pricesInOrn?.[baseAssetAddress];
if (!baseAssetPriceInOrn) throw new Error(`Base asset price ${baseAssetName} in ORN not found`);
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) throw new Error('Base currency price in ORN not found');
if (baseCurrencyPriceInOrn === undefined) 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`);
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 { orionFeeInFeeAsset, networkFeeInFeeAsset, totalFeeInFeeAsset } = utils.calculateFeeInFeeAsset(
swapInfo.orderInfo.amount,

View File

@@ -4,7 +4,7 @@ import { ethers } from 'ethers';
import { Exchange__factory } from '@orionprotocol/contracts';
import getBalances from '../../utils/getBalances';
import BalanceGuard from '../../BalanceGuard';
import OrionUnit from '..';
import type OrionUnit from '..';
import { utils } from '../..';
import {
INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION, WITHDRAW_GAS_LIMIT,
@@ -13,11 +13,11 @@ import { normalizeNumber } from '../../utils';
import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
import simpleFetch from '../../simpleFetch';
export type WithdrawParams = {
asset: string,
amount: BigNumber.Value,
signer: ethers.Signer,
orionUnit: OrionUnit,
export interface WithdrawParams {
asset: string
amount: BigNumber.Value
signer: ethers.Signer
orionUnit: OrionUnit
}
export default async function withdraw({
@@ -29,8 +29,8 @@ export default async function withdraw({
if (asset === '') throw new Error('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`);
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 walletAddress = await signer.getAddress();
@@ -47,7 +47,7 @@ export default async function withdraw({
const gasPriceWei = await simpleFetch(orionBlockchain.getGasPriceWei)();
const assetAddress = assetToAddress[asset];
if (!assetAddress) throw new Error(`Asset '${asset}' not found`);
if (assetAddress === undefined) throw new Error(`Asset '${asset}' not found`);
const balances = await getBalances(
{
@@ -76,7 +76,7 @@ export default async function withdraw({
name: asset,
address: assetAddress,
},
amount: amount.toString(),
amount: amountBN.toString(),
sources: ['exchange'],
});
@@ -112,7 +112,7 @@ export default async function withdraw({
const txResponse = await provider.sendTransaction(signedTx);
console.log(`Withdraw tx sent: ${txResponse.hash}. Waiting for confirmation...`);
const txReceipt = await txResponse.wait();
if (txReceipt.status) {
if (txReceipt.status !== undefined) {
console.log('Withdraw tx confirmed');
} else {
console.log('Withdraw tx failed');

View File

@@ -1,7 +1,7 @@
import { Exchange__factory, IUniswapV2Pair__factory, IUniswapV2Router__factory } from '@orionprotocol/contracts';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import OrionUnit from '..';
import type OrionUnit from '..';
import BalanceGuard from '../../BalanceGuard';
import { ADD_LIQUIDITY_GAS_LIMIT, INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION } from '../../constants';
import simpleFetch from '../../simpleFetch';
@@ -11,15 +11,15 @@ import getNativeCryptocurrency from '../../utils/getNativeCryptocurrency';
const ADD_LIQUIDITY_SLIPPAGE = 0.05;
export type AddLiquidityParams = {
poolName: string,
amountAsset: string,
amount: BigNumber.Value,
export interface AddLiquidityParams {
poolName: string
amountAsset: string
amount: BigNumber.Value
signer: ethers.Signer
}
export type RemoveAllLiquidityParams = {
poolName: string,
export interface RemoveAllLiquidityParams {
poolName: string
signer: ethers.Signer
}
@@ -57,14 +57,14 @@ export default class FarmingManager {
.connect(exchangeContractAddress, this.orionUnit.provider);
const assetAAddress = assetToAddress[assetA];
if (!assetAAddress) throw new Error(`Asset '${assetA}' not found`);
if (assetAAddress === undefined) throw new Error(`Asset '${assetA}' not found`);
const assetBAddress = assetToAddress[assetB];
if (!assetBAddress) throw new Error(`Asset '${assetB}' not found`);
if (assetBAddress === undefined) throw new Error(`Asset '${assetB}' not found`);
const assetADecimals = assetToDecimals[assetA];
if (!assetADecimals) throw new Error(`Decimals for asset '${assetA}' not found`);
if (assetADecimals === undefined) throw new Error(`Decimals for asset '${assetA}' not found`);
const assetBDecimals = assetToDecimals[assetB];
if (!assetBDecimals) throw new Error(`Decimals for asset '${assetB}' not found`);
if (assetBDecimals === undefined) throw new Error(`Decimals for asset '${assetB}' not found`);
const nativeCryptocurrency = getNativeCryptocurrency(assetToAddress);
const balances = await getBalances(
@@ -90,7 +90,7 @@ export default class FarmingManager {
const poolsConfig = await simpleFetch(this.orionUnit.orionBlockchain.getPoolsConfig)();
const pool = poolsConfig.pools[poolName];
if (!pool) throw new Error(`Pool ${poolName} not found`);
if (pool == null) throw new Error(`Pool ${poolName} not found`);
const pairContract = IUniswapV2Pair__factory
.connect(pool.lpTokenAddress, this.orionUnit.provider);
@@ -145,7 +145,7 @@ export default class FarmingManager {
sources: ['exchange', 'wallet'],
});
const unsignedTx = await exchangeContract?.populateTransaction.withdrawToPool(
const unsignedTx = await exchangeContract.populateTransaction.withdrawToPool(
assetBIsNativeCurrency ? assetBAddress : assetAAddress,
assetBIsNativeCurrency ? assetAAddress : assetBAddress,
assetBIsNativeCurrency
@@ -183,7 +183,7 @@ export default class FarmingManager {
if (assetAIsNativeCurrency || assetBIsNativeCurrency) {
const contractBalance = balances[nativeCryptocurrency]?.exchange;
if (!contractBalance) throw new Error(`No balance for '${nativeCryptocurrency}'`);
if (contractBalance == null) throw new Error(`No balance for '${nativeCryptocurrency}'`);
const nativeAssetAmount = assetBIsNativeCurrency ? assetBAmount : assetAAmount;
if (nativeAssetAmount.gt(contractBalance)) {
@@ -231,18 +231,18 @@ export default class FarmingManager {
} = await simpleFetch(this.orionUnit.orionBlockchain.getInfo)();
const assetAAddress = assetToAddress[assetA];
if (!assetAAddress) throw new Error(`Asset '${assetA}' not found`);
if (assetAAddress === undefined) throw new Error(`Asset '${assetA}' not found`);
const assetBAddress = assetToAddress[assetB];
if (!assetBAddress) throw new Error(`Asset '${assetB}' not found`);
if (assetBAddress === undefined) throw new Error(`Asset '${assetB}' not found`);
const assetADecimals = assetToDecimals[assetA];
if (!assetADecimals) throw new Error(`Decimals for asset '${assetA}' not found`);
if (assetADecimals === undefined) throw new Error(`Decimals for asset '${assetA}' not found`);
const assetBDecimals = assetToDecimals[assetB];
if (!assetBDecimals) throw new Error(`Decimals for asset '${assetB}' not found`);
if (assetBDecimals === undefined) throw new Error(`Decimals for asset '${assetB}' not found`);
const poolsConfig = await simpleFetch(this.orionUnit.orionBlockchain.getPoolsConfig)();
const pool = poolsConfig.pools[poolName];
if (!pool) throw new Error(`Pool ${poolName} not found`);
if (pool == null) throw new Error(`Pool ${poolName} not found`);
const walletAddress = await signer.getAddress();

View File

@@ -6,7 +6,7 @@ import type { SupportedChainId, VerboseOrionUnitConfig } from '../types';
import Exchange from './Exchange';
import FarmingManager from './FarmingManager';
import { chains } from '../config';
import { networkCodes } from '../constants';
import { type networkCodes } from '../constants';
// type KnownConfig = {
// env: string;
@@ -42,7 +42,7 @@ export default class OrionUnit {
constructor(config: VerboseOrionUnitConfig) {
this.config = config;
const chainInfo = chains[config.chainId];
if (!chainInfo) throw new Error('Chain info is required');
if (chainInfo === undefined) throw new Error('Chain info is required');
// if ('env' in config)
// this.env = config.env;

View File

@@ -1,4 +1,4 @@
import exchanges from './exchanges';
import type exchanges from './exchanges';
const mapping: Record<
typeof exchanges[number],

View File

@@ -1,4 +1,4 @@
import { SupportedChainId } from '../types';
import { type SupportedChainId } from '../types';
import eip712DomainData from '../config/eip712DomainData.json';
import eip712DomainSchema from '../config/schemas/eip712DomainSchema';

View File

@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { CFDOrder } from '../types';
import { type CFDOrder } from '../types';
const hashCFDOrder = (order: CFDOrder) => ethers.utils.solidityKeccak256(
[
@@ -24,7 +24,7 @@ const hashCFDOrder = (order: CFDOrder) => ethers.utils.solidityKeccak256(
order.matcherFee,
order.nonce,
order.expiration,
order.buySide ? '0x01' : '0x00',
order.buySide === 1 ? '0x01' : '0x00',
],
);

View File

@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { Order } from '../types';
import { type Order } from '../types';
const hashOrder = (order: Order) => ethers.utils.solidityKeccak256(
[
@@ -28,7 +28,7 @@ const hashOrder = (order: Order) => ethers.utils.solidityKeccak256(
order.matcherFee,
order.nonce,
order.expiration,
order.buySide ? '0x01' : '0x00',
order.buySide === 1 ? '0x01' : '0x00',
],
);

View File

@@ -1,10 +1,10 @@
/* eslint-disable no-underscore-dangle */
import { TypedDataSigner } from '@ethersproject/abstract-signer';
import { type TypedDataSigner } from '@ethersproject/abstract-signer';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { type ethers } from 'ethers';
import { joinSignature, splitSignature } from 'ethers/lib/utils';
import { INTERNAL_ORION_PRECISION } from '../constants';
import { CFDOrder, SignedCFDOrder, SupportedChainId } from '../types';
import { type CFDOrder, type SignedCFDOrder, type SupportedChainId } from '../types';
import normalizeNumber from '../utils/normalizeNumber';
import getDomainData from './getDomainData';
import signCFDOrderPersonal from './signCFDOrderPersonal';

View File

@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { CFDOrder } from '../types';
import { type CFDOrder } from '../types';
const { arrayify, joinSignature, splitSignature } = ethers.utils;

View File

@@ -1,9 +1,9 @@
/* eslint-disable no-underscore-dangle */
import { TypedDataSigner } from '@ethersproject/abstract-signer';
import { ethers } from 'ethers';
import { type TypedDataSigner } from '@ethersproject/abstract-signer';
import { type ethers } from 'ethers';
import { joinSignature, splitSignature } from 'ethers/lib/utils';
import CANCEL_ORDER_TYPES from '../constants/cancelOrderTypes';
import { CancelOrderRequest, SignedCancelOrderRequest, SupportedChainId } from '../types';
import { type CancelOrderRequest, type SignedCancelOrderRequest, type SupportedChainId } from '../types';
import getDomainData from './getDomainData';
import signCancelOrderPersonal from './signCancelOrderPersonal';

View File

@@ -1,6 +1,6 @@
import { ethers } from 'ethers';
import { arrayify, joinSignature, splitSignature } from 'ethers/lib/utils';
import { CancelOrderRequest } from '../types';
import { type CancelOrderRequest } from '../types';
const signCancelOrderPersonal = async (
cancelOrderRequest: CancelOrderRequest,

View File

@@ -1,11 +1,11 @@
/* eslint-disable no-underscore-dangle */
import { TypedDataSigner } from '@ethersproject/abstract-signer';
import { type TypedDataSigner } from '@ethersproject/abstract-signer';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { type ethers } from 'ethers';
import { joinSignature, splitSignature } from 'ethers/lib/utils';
import { INTERNAL_ORION_PRECISION } from '../constants';
import ORDER_TYPES from '../constants/orderTypes';
import { Order, SignedOrder, SupportedChainId } from '../types';
import { type Order, type SignedOrder, type SupportedChainId } from '../types';
import normalizeNumber from '../utils/normalizeNumber';
import getDomainData from './getDomainData';
import hashOrder from './hashOrder';

View File

@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { Order } from '../types';
import { type Order } from '../types';
const { arrayify, joinSignature, splitSignature } = ethers.utils;

View File

@@ -1,4 +1,4 @@
import { Schema, z } from 'zod';
import { type Schema, type z } from 'zod';
import fetch from 'isomorphic-unfetch';
import {
@@ -26,10 +26,10 @@ export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, Err
// payload
const fetchResult = await fromPromise(fetch(url, {
...options || {},
...options ?? {},
headers: {
'Cache-Control': 'no-store, max-age=0',
...(options ? options.headers : {}),
...(options !== undefined ? options.headers : {}),
},
}), (e) => {
if (e instanceof Error) {
@@ -111,7 +111,7 @@ export default async function fetchWithValidation<DataOut, DataIn, ErrorOut, Err
const json: unknown = jsonResult.value;
if (response.status >= 400) { // Client error
if (errorSchema) {
if (errorSchema !== undefined) {
const serverError = errorSchema.safeParse(json);
if (serverError.success) {
return err({

View File

@@ -1,4 +1,4 @@
import BigNumber from 'bignumber.js';
import type BigNumber from 'bignumber.js';
import { z } from 'zod';
import fetchWithValidation from '../../fetchWithValidation';
import swapInfoSchema from './schemas/swapInfoSchema';
@@ -9,12 +9,12 @@ import errorSchema from './schemas/errorSchema';
import placeAtomicSwapSchema from './schemas/placeAtomicSwapSchema';
import { OrionAggregatorWS } from './ws';
import { atomicSwapHistorySchema } from './schemas/atomicSwapHistorySchema';
import { Exchange, SignedCancelOrderRequest, SignedCFDOrder, SignedOrder } from '../../types';
import { type Exchange, type SignedCancelOrderRequest, type SignedCFDOrder, type SignedOrder } from '../../types';
import { pairConfigSchema } from './schemas';
import {
aggregatedOrderbookSchema, exchangeOrderbookSchema, poolReservesSchema,
} from './schemas/aggregatedOrderbookSchema';
import networkCodes from '../../constants/networkCodes';
import type networkCodes from '../../constants/networkCodes';
import toUpperCase from '../../utils/toUpperCase';
import httpToWS from '../../utils/httpToWS';
@@ -146,7 +146,7 @@ class OrionAggregator {
const headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
...partnerId && { 'X-Partner-Id': partnerId },
...(partnerId !== undefined) && { 'X-Partner-Id': partnerId },
};
return fetchWithValidation(
@@ -232,7 +232,7 @@ class OrionAggregator {
} else {
url.searchParams.append('amountOut', amount);
}
if (exchanges) {
if (exchanges !== undefined) {
if (Array.isArray(exchanges)) {
exchanges.forEach((exchange) => {
url.searchParams.append('exchanges', exchange);
@@ -241,7 +241,7 @@ class OrionAggregator {
url.searchParams.append('exchanges', exchanges);
}
}
if (instantSettlement) {
if (instantSettlement !== undefined && instantSettlement) {
url.searchParams.append('instantSettlement', 'true');
}

View File

@@ -10,112 +10,112 @@ import {
} from './schemas';
import UnsubscriptionType from './UnsubscriptionType';
import {
SwapInfoByAmountIn, SwapInfoByAmountOut, SwapInfoBase,
AssetPairUpdate, OrderbookItem, Balance, Exchange, CFDBalance,
type SwapInfoByAmountIn, type SwapInfoByAmountOut, type SwapInfoBase,
type AssetPairUpdate, type OrderbookItem, type Balance, type Exchange, type CFDBalance,
} from '../../../types';
import unsubscriptionDoneSchema from './schemas/unsubscriptionDoneSchema';
import assetPairConfigSchema from './schemas/assetPairConfigSchema';
import { fullOrderSchema, orderUpdateSchema } from './schemas/addressUpdateSchema';
import { type fullOrderSchema, type orderUpdateSchema } from './schemas/addressUpdateSchema';
import cfdAddressUpdateSchema from './schemas/cfdAddressUpdateSchema';
// import errorSchema from './schemas/errorSchema';
const UNSUBSCRIBE = 'u';
// https://github.com/orionprotocol/orion-aggregator/tree/feature/OP-1752-symmetric-swap#swap-info-subscribe
type SwapSubscriptionRequest = {
interface SwapSubscriptionRequest {
// d: string, // swap request UUID, set by client side
i: string, // asset in
o: string, // asset out
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)
e?: boolean; // is amount IN? Value `false` means a = amount OUT, `true` if omitted
is?: boolean; // instant settlement
es?: Exchange[] | '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
}
type BrokerTradableAtomicSwapBalanceSubscription = {
callback: (balances: Partial<Record<string, number>>) => void,
interface BrokerTradableAtomicSwapBalanceSubscription {
callback: (balances: Partial<Record<string, number>>) => void
}
type PairsConfigSubscription = {
interface PairsConfigSubscription {
callback: ({ kind, data }: {
kind: 'initial' | 'update',
data: Partial<Record<string, AssetPairUpdate>>,
}) => void,
kind: 'initial' | 'update'
data: Partial<Record<string, AssetPairUpdate>>
}) => void
}
type PairConfigSubscription = {
payload: string,
interface PairConfigSubscription {
payload: string
callback: ({ kind, data }: {
kind: 'initial' | 'update',
data: AssetPairUpdate,
}) => void,
kind: 'initial' | 'update'
data: AssetPairUpdate
}) => void
}
type AggregatedOrderbookSubscription = {
payload: string,
interface AggregatedOrderbookSubscription {
payload: string
callback: (
asks: OrderbookItem[],
bids: OrderbookItem[],
pair: string
) => void,
) => void
}
type SwapInfoSubscription = {
payload: SwapSubscriptionRequest,
callback: (swapInfo: SwapInfoByAmountIn | SwapInfoByAmountOut) => void,
interface SwapInfoSubscription {
payload: SwapSubscriptionRequest
callback: (swapInfo: SwapInfoByAmountIn | SwapInfoByAmountOut) => void
}
type AddressUpdateUpdate = {
kind: 'update',
interface AddressUpdateUpdate {
kind: 'update'
balances: Partial<
Record<
string,
Balance
>
>,
Record<
string,
Balance
>
>
order?: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema>
}
type AddressUpdateInitial = {
kind: 'initial',
interface AddressUpdateInitial {
kind: 'initial'
balances: Partial<
Record<
string,
Balance
>
>,
orders?: z.infer<typeof fullOrderSchema>[] // The field is not defined if the user has no orders
Record<
string,
Balance
>
>
orders?: Array<z.infer<typeof fullOrderSchema>> // The field is not defined if the user has no orders
}
type CfdAddressUpdateUpdate = {
kind: 'update',
balances?: CFDBalance[],
interface CfdAddressUpdateUpdate {
kind: 'update'
balances?: CFDBalance[]
order?: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema>
}
type CfdAddressUpdateInitial = {
kind: 'initial',
balances: CFDBalance[],
orders?: z.infer<typeof fullOrderSchema>[] // The field is not defined if the user has no orders
interface CfdAddressUpdateInitial {
kind: 'initial'
balances: CFDBalance[]
orders?: Array<z.infer<typeof fullOrderSchema>> // The field is not defined if the user has no orders
}
type AddressUpdateSubscription = {
payload: string,
callback: (data: AddressUpdateUpdate | AddressUpdateInitial) => void,
interface AddressUpdateSubscription {
payload: string
callback: (data: AddressUpdateUpdate | AddressUpdateInitial) => void
}
type CfdAddressUpdateSubscription = {
payload: string,
callback: (data: CfdAddressUpdateUpdate | CfdAddressUpdateInitial) => void,
interface CfdAddressUpdateSubscription {
payload: string
callback: (data: CfdAddressUpdateUpdate | CfdAddressUpdateInitial) => void
}
type Subscription = {
[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE]: AddressUpdateSubscription,
[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE]: CfdAddressUpdateSubscription,
[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE]: AggregatedOrderbookSubscription,
[SubscriptionType.ASSET_PAIRS_CONFIG_UPDATES_SUBSCRIBE]: PairsConfigSubscription,
[SubscriptionType.ASSET_PAIR_CONFIG_UPDATES_SUBSCRIBE]: PairConfigSubscription,
[SubscriptionType.BROKER_TRADABLE_ATOMIC_SWAP_ASSETS_BALANCE_UPDATES_SUBSCRIBE]: BrokerTradableAtomicSwapBalanceSubscription,
interface Subscription {
[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE]: AddressUpdateSubscription
[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE]: CfdAddressUpdateSubscription
[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE]: AggregatedOrderbookSubscription
[SubscriptionType.ASSET_PAIRS_CONFIG_UPDATES_SUBSCRIBE]: PairsConfigSubscription
[SubscriptionType.ASSET_PAIR_CONFIG_UPDATES_SUBSCRIBE]: PairConfigSubscription
[SubscriptionType.BROKER_TRADABLE_ATOMIC_SWAP_ASSETS_BALANCE_UPDATES_SUBSCRIBE]: BrokerTradableAtomicSwapBalanceSubscription
[SubscriptionType.SWAP_SUBSCRIBE]: SwapInfoSubscription
}
@@ -133,14 +133,14 @@ type BufferLike =
| Uint8Array
| ArrayBuffer
| SharedArrayBuffer
| ReadonlyArray<unknown>
| ReadonlyArray<number>
| { valueOf(): ArrayBuffer }
| { valueOf(): SharedArrayBuffer }
| { valueOf(): Uint8Array }
| { valueOf(): ReadonlyArray<number> }
| { valueOf(): string }
| { [Symbol.toPrimitive](hint: string): string };
| readonly unknown[]
| readonly number[]
| { valueOf: () => ArrayBuffer }
| { valueOf: () => SharedArrayBuffer }
| { valueOf: () => Uint8Array }
| { valueOf: () => readonly number[] }
| { valueOf: () => string }
| { [Symbol.toPrimitive]: (hint: string) => string };
const isSubType = (subType: string): subType is keyof Subscription => Object.values(SubscriptionType).some((t) => t === subType);
class OrionAggregatorWS {
@@ -206,10 +206,10 @@ class OrionAggregatorWS {
type: T,
subscription: Subscription[T],
) {
if (!this.ws) this.init();
if (this.ws == null) this.init();
const isExclusive = exclusiveSubscriptions.some((t) => t === type);
const subs = this.subscriptions[type];
if (isExclusive && subs && Object.keys(subs).length > 0) {
if (isExclusive && (subs != null) && Object.keys(subs).length > 0) {
throw new Error(`Subscription '${type}' already exists. Please unsubscribe first.`);
}
@@ -252,18 +252,18 @@ class OrionAggregatorWS {
if (subscription.includes('0x')) { // is wallet address (ADDRESS_UPDATE)
const auSubscriptions = this.subscriptions[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE];
if (auSubscriptions) {
if (auSubscriptions != null) {
const targetAuSub = Object.entries(auSubscriptions).find(([, value]) => value?.payload === subscription);
if (targetAuSub) {
if (targetAuSub != null) {
const [key] = targetAuSub;
delete this.subscriptions[SubscriptionType.ADDRESS_UPDATES_SUBSCRIBE]?.[key];
}
}
const aufSubscriptions = this.subscriptions[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE];
if (aufSubscriptions) {
if (aufSubscriptions != null) {
const targetAufSub = Object.entries(aufSubscriptions).find(([, value]) => value?.payload === subscription);
if (targetAufSub) {
if (targetAufSub != null) {
const [key] = targetAufSub;
delete this.subscriptions[SubscriptionType.CFD_ADDRESS_UPDATES_SUBSCRIBE]?.[key];
}
@@ -275,9 +275,9 @@ class OrionAggregatorWS {
// !!! swap info subscription is uuid that contains hyphen
} else if (subscription.includes('-') && subscription.split('-').length === 2) { // is pair name(AGGREGATED_ORDER_BOOK_UPDATE)
const aobSubscriptions = this.subscriptions[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE];
if (aobSubscriptions) {
if (aobSubscriptions != null) {
const targetAobSub = Object.entries(aobSubscriptions).find(([, value]) => value?.payload === subscription);
if (targetAobSub) {
if (targetAobSub != null) {
const [key] = targetAobSub;
delete this.subscriptions[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE]?.[key];
}
@@ -314,10 +314,10 @@ class OrionAggregatorWS {
.filter(isSubType)
.forEach((subType) => {
const subscriptions = subscriptionsToReconnect[subType];
if (subscriptions) {
if (subscriptions != null) {
Object.keys(subscriptions).forEach((subKey) => {
const sub = subscriptions[subKey];
if (sub) this.subscribe(subType, sub);
if (sub != null) this.subscribe(subType, sub);
});
}
});
@@ -326,8 +326,9 @@ class OrionAggregatorWS {
};
this.ws.onmessage = (e) => {
const { data } = e;
this.logger?.(`OrionAggregatorWS: received message: ${e.data.toString()}`);
const rawJson: unknown = JSON.parse(data.toString());
if (typeof data !== 'string') return;
this.logger?.(`OrionAggregatorWS: received message: ${data}`);
const rawJson: unknown = JSON.parse(data);
const messageSchema = z.union([
initMessageSchema,
@@ -371,7 +372,7 @@ class OrionAggregatorWS {
path: json.ps,
exchanges: json.e,
poolOptimal: json.po,
...json.oi && {
...(json.oi != null) && {
orderInfo: {
pair: json.oi.p,
side: json.oi.s,
@@ -485,8 +486,8 @@ class OrionAggregatorWS {
case MessageType.CFD_ADDRESS_UPDATE:
switch (json.k) { // message kind
case 'i': { // initial
const fullOrders = json.o
? json.o.reduce<z.infer<typeof fullOrderSchema>[]>((prev, o) => {
const fullOrders = (json.o != null)
? json.o.reduce<Array<z.infer<typeof fullOrderSchema>>>((prev, o) => {
prev.push(o);
return prev;
@@ -498,13 +499,13 @@ class OrionAggregatorWS {
]?.[json.id]?.callback({
kind: 'initial',
orders: fullOrders,
balances: json.b ?? [],
balances: json.b,
});
}
break;
case 'u': { // update
let orderUpdate: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema> | undefined;
if (json.o) {
if (json.o != null) {
const firstOrder = json.o[0];
orderUpdate = firstOrder;
}
@@ -523,23 +524,23 @@ class OrionAggregatorWS {
}
break;
case MessageType.ADDRESS_UPDATE: {
const balances = json.b
const balances = (json.b != null)
? Object.entries(json.b)
.reduce<Partial<Record<string, Balance>>>((prev, [asset, assetBalances]) => {
if (!assetBalances) return prev;
const [tradable, reserved, contract, wallet, allowance] = assetBalances;
if (assetBalances == null) return prev;
const [tradable, reserved, contract, wallet, allowance] = assetBalances;
prev[asset] = {
tradable, reserved, contract, wallet, allowance,
};
prev[asset] = {
tradable, reserved, contract, wallet, allowance,
};
return prev;
}, {})
return prev;
}, {})
: {};
switch (json.k) { // message kind
case 'i': { // initial
const fullOrders = json.o
? json.o.reduce<z.infer<typeof fullOrderSchema>[]>((prev, o) => {
const fullOrders = (json.o != null)
? json.o.reduce<Array<z.infer<typeof fullOrderSchema>>>((prev, o) => {
prev.push(o);
return prev;
@@ -557,7 +558,7 @@ class OrionAggregatorWS {
break;
case 'u': { // update
let orderUpdate: z.infer<typeof orderUpdateSchema> | z.infer<typeof fullOrderSchema> | undefined;
if (json.o) {
if (json.o != null) {
const firstOrder = json.o[0];
orderUpdate = firstOrder;
}

View File

@@ -2,7 +2,7 @@ import fetchWithValidation from '../../fetchWithValidation';
import overviewSchema from './schemas/overviewSchema';
export default class OrionAnalytics {
private apiUrl: string;
private readonly apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;

View File

@@ -3,62 +3,62 @@ import fetchWithValidation from '../../fetchWithValidation';
import {
IDOSchema, atomicHistorySchema,
poolsConfigSchema, poolsInfoSchema, infoSchema, historySchema,
addPoolSchema, adminPoolsListSchema, adminPoolSchema,
type addPoolSchema, adminPoolsListSchema, adminPoolSchema,
atomicSummarySchema,
poolsLpAndStakedSchema,
userVotesSchema,
userEarnedSchema,
PairStatusEnum,
type PairStatusEnum,
pairStatusSchema,
cfdContractsSchema,
cfdHistorySchema,
} from './schemas';
import redeemOrderSchema from '../OrionAggregator/schemas/redeemOrderSchema';
import type redeemOrderSchema from '../OrionAggregator/schemas/redeemOrderSchema';
import { sourceAtomicHistorySchema, targetAtomicHistorySchema } from './schemas/atomicHistorySchema';
import { makePartial } from '../../utils';
import { networkCodes } from '../../constants';
import { type networkCodes } from '../../constants';
interface IAdminAuthHeaders {
auth: string;
auth: string
[key: string]: string
}
export interface IEditPool {
tokenAIcon?: string;
tokenBIcon?: string;
symbol?: string;
status: PairStatusEnum;
minQty?: number;
tokenASymbol?: string;
tokenBSymbol?: string;
tokensReversed?: boolean;
tokenAIcon?: string
tokenBIcon?: string
symbol?: string
status: PairStatusEnum
minQty?: number
tokenASymbol?: string
tokenBSymbol?: string
tokensReversed?: boolean
}
type AtomicSwapHistoryBaseQuery = {
interface AtomicSwapHistoryBaseQuery extends Partial<Record<string, string | number>> {
limit?: number
sender?: string,
receiver?: string,
used?: 0 | 1,
page?: number,
sourceNetworkCode?: typeof networkCodes[number],
sender?: string
receiver?: string
used?: 0 | 1
page?: number
sourceNetworkCode?: typeof networkCodes[number]
}
type AtomicSwapHistorySourceQuery = AtomicSwapHistoryBaseQuery & {
type?: 'source',
expiredLock?: 0 | 1,
state?: 'LOCKED' | 'CLAIMED' |'REFUNDED',
type?: 'source'
expiredLock?: 0 | 1
state?: 'LOCKED' | 'CLAIMED' | 'REFUNDED'
}
type AtomicSwapHistoryTargetQuery = AtomicSwapHistoryBaseQuery & {
type?: 'target',
expiredRedeem?: 0 | 1,
state?: 'REDEEMED' | 'BEFORE-REDEEM',
type?: 'target'
expiredRedeem?: 0 | 1
state?: 'REDEEMED' | 'BEFORE-REDEEM'
}
type CfdHistoryQuery = {
instrument?: string,
page?: number,
limit?: number,
interface CfdHistoryQuery extends Partial<Record<string, string | number>> {
instrument?: string
page?: number
limit?: number
}
class OrionBlockchain {
private readonly apiUrl: string;
@@ -110,12 +110,12 @@ class OrionBlockchain {
return `${this.apiUrl}/`;
}
private getSummaryRedeem = (brokerAddress: string, unshifted?: 1 | 0, sourceNetworkCode?: typeof networkCodes[number]) => {
private readonly getSummaryRedeem = (brokerAddress: string, unshifted?: 1 | 0, sourceNetworkCode?: typeof networkCodes[number]) => {
const url = new URL(`${this.apiUrl}/api/atomic/summary-redeem/${brokerAddress}`);
if (unshifted) {
if (unshifted !== undefined && unshifted === 1) {
url.searchParams.append('unshifted', unshifted.toString());
}
if (sourceNetworkCode) {
if (sourceNetworkCode !== undefined) {
url.searchParams.append('sourceNetworkCode', sourceNetworkCode);
}
return fetchWithValidation(
@@ -124,12 +124,12 @@ class OrionBlockchain {
);
};
private getSummaryClaim = (brokerAddress: string) => fetchWithValidation(
private readonly getSummaryClaim = (brokerAddress: string) => fetchWithValidation(
`${this.apiUrl}/api/atomic/summary-claim/${brokerAddress}`,
atomicSummarySchema,
);
private getQueueLength = () => fetchWithValidation(
private readonly getQueueLength = () => fetchWithValidation(
`${this.apiUrl}/api/queueLength`,
z.number().int(),
);
@@ -357,7 +357,10 @@ class OrionBlockchain {
const url = new URL(`${this.apiUrl}/api/atomic/history/`);
Object.entries(query)
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
.forEach(([key, value]) => {
if (value === undefined) throw new Error('Value must be defined');
url.searchParams.append(key, value.toString());
});
return fetchWithValidation(url.toString(), atomicHistorySchema);
};
@@ -366,9 +369,12 @@ class OrionBlockchain {
const url = new URL(`${this.apiUrl}/api/atomic/history/`);
Object.entries(query)
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
.forEach(([key, value]) => {
if (value === undefined) throw new Error('Value must be defined');
url.searchParams.append(key, value.toString());
});
if (!query.type) url.searchParams.append('type', 'source');
if (query.type !== undefined) url.searchParams.append('type', 'source');
return fetchWithValidation(url.toString(), sourceAtomicHistorySchema);
};
@@ -377,9 +383,12 @@ class OrionBlockchain {
const url = new URL(`${this.apiUrl}/api/atomic/history/`);
Object.entries(query)
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
.forEach(([key, value]) => {
if (value === undefined) throw new Error('Value must be defined');
url.searchParams.append(key, value.toString());
});
if (!query.type) url.searchParams.append('type', 'target');
if (query.type !== undefined) url.searchParams.append('type', 'target');
return fetchWithValidation(url.toString(), targetAtomicHistorySchema);
};
@@ -406,7 +415,10 @@ class OrionBlockchain {
const url = new URL(`${this.apiUrl}/api/cfd/deposit-withdraw/${address}`);
Object.entries(query)
.forEach(([key, value]) => url.searchParams.append(key, value.toString()));
.forEach(([key, value]) => {
if (value === undefined) throw new Error('Value must be defined');
url.searchParams.append(key, value.toString());
});
return fetchWithValidation(url.toString(), cfdHistorySchema);
};

View File

@@ -1,11 +1,11 @@
import fetchWithValidation from '../../fetchWithValidation';
import { Exchange } from '../../types';
import { type Exchange } from '../../types';
import { statisticsOverviewSchema, topPairsStatisticsSchema } from './schemas';
import candlesSchema from './schemas/candlesSchema';
import { PriceFeedWS } from './ws';
class PriceFeed {
private apiUrl: string;
private readonly apiUrl: string;
readonly ws: PriceFeedWS;

View File

@@ -5,13 +5,13 @@ import priceFeedSubscriptions from './priceFeedSubscriptions';
import { tickerInfoSchema, candleSchema } from './schemas';
import priceSchema from './schemas/priceSchema';
type TickerInfo = {
pairName: string;
lastPrice: string;
openPrice: string;
highPrice: string;
lowPrice: string;
volume24h: string;
interface TickerInfo {
pairName: string
lastPrice: string
openPrice: string
highPrice: string
lowPrice: string
volume24h: string
}
const allTickersSchema = z.unknown().array()
@@ -57,11 +57,11 @@ export type Subscription<
Schema = z.infer<typeof subscriptions[T]['schema']>
> = typeof subscriptions[T] extends { payload: true }
? {
callback: (data: Schema) => void,
payload: string,
} : {
callback: (data: Schema) => void,
}
callback: (data: Schema) => void
payload: string
} : {
callback: (data: Schema) => void
}
export default class PriceFeedSubscription<T extends SubscriptionType = SubscriptionType> {
public readonly id: string;
@@ -104,22 +104,24 @@ export default class PriceFeedSubscription<T extends SubscriptionType = Subscrip
this.isClosedIntentionally = false;
const { payload, url, type } = this;
this.ws = new WebSocket(`${url}/${type}${payload ? `/${payload.toString()}` : ''}`);
this.ws = new WebSocket(`${url}/${type}${payload !== undefined ? `/${payload.toString()}` : ''}`);
this.ws.onmessage = (e) => {
if (e.data === 'pong') return;
const json: unknown = JSON.parse(e.data.toString());
const { data } = e;
if (typeof data !== 'string') return;
if (data === 'pong') return;
const json: unknown = JSON.parse(data);
const subscription = subscriptions[type];
const parseResult = subscription.schema.safeParse(json);
if (parseResult.success === false) {
if (!parseResult.success) {
const errorsMessage = parseResult.error.errors.map((error) => `[${error.path.join('.')}] ${error.message}`).join(', ');
throw new Error(`Can't recognize PriceFeed "${type}" subscription message "${e.data.toString()}": ${errorsMessage}`);
throw new Error(`Can't recognize PriceFeed "${type}" subscription message "${data}": ${errorsMessage}`);
}
this.callback(parseResult.data);
};
this.ws.onclose = () => {
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
if (this.heartbeatInterval != null) clearInterval(this.heartbeatInterval);
if (!this.isClosedIntentionally) this.init();
};

View File

@@ -1,4 +1,4 @@
import PriceFeedSubscription, { SubscriptionType, Subscription } from './PriceFeedSubscription';
import PriceFeedSubscription, { type SubscriptionType, type Subscription } from './PriceFeedSubscription';
export * as schemas from './schemas';
export class PriceFeedWS {
@@ -11,7 +11,7 @@ export class PriceFeedWS {
>;
}> = {};
private url: string;
private readonly url: string;
constructor(url: string) {
this.url = url;
@@ -36,7 +36,7 @@ export class PriceFeedWS {
return {
type: sub.type,
id: sub.id,
unsubscribe: () => this.unsubscribe(sub.type, sub.id),
unsubscribe: () => { this.unsubscribe(sub.type, sub.id); },
};
}

View File

@@ -5,22 +5,22 @@ import distinctAnalyticsSchema from './schemas/distinctAnalyticsSchema';
import globalAnalyticsSchema from './schemas/globalAnalyticsSchema';
import linkSchema from './schemas/linkSchema';
type CreateLinkPayloadType = {
referer: string;
link_option: number;
};
type SubscribePayloadType = {
ref_target: string;
referral: string;
interface CreateLinkPayloadType {
referer: string
link_option: number
}
type SignatureType = {
signature: string;
};
interface SubscribePayloadType {
ref_target: string
referral: string
}
interface SignatureType {
signature: string
}
class ReferralSystem {
private apiUrl: string;
private readonly apiUrl: string;
get api() {
return this.apiUrl;

View File

@@ -1,4 +1,4 @@
import { Schema, z } from 'zod';
import { type Schema, type z } from 'zod';
import fetchWithValidation from './fetchWithValidation';
// https://stackoverflow.com/a/64919133

View File

@@ -1,119 +1,119 @@
import BigNumber from 'bignumber.js';
import exchanges from './constants/exchanges';
import subOrderStatuses from './constants/subOrderStatuses';
import positionStatuses from './constants/positionStatuses';
import type BigNumber from 'bignumber.js';
import type exchanges from './constants/exchanges';
import type subOrderStatuses from './constants/subOrderStatuses';
import type positionStatuses from './constants/positionStatuses';
export type DeepPartial<T> = T extends object ? {
[P in keyof T]?: DeepPartial<T[P]>;
} : T;
export type AssetPairUpdate = {
minQty: number,
pricePrecision: number,
export interface AssetPairUpdate {
minQty: number
pricePrecision: number
}
export type SubOrder = {
pair: string,
exchange: string,
id: number,
amount: number,
settledAmount: number,
price: number,
status: typeof subOrderStatuses[number],
side: 'BUY' | 'SELL',
export interface SubOrder {
pair: string
exchange: string
id: number
amount: number
settledAmount: number
price: number
status: typeof subOrderStatuses[number]
side: 'BUY' | 'SELL'
subOrdQty: number
}
export type Balance = {
tradable: string,
reserved: string,
contract: string,
wallet: string,
allowance: string,
export interface Balance {
tradable: string
reserved: string
contract: string
wallet: string
allowance: string
}
export type PositionStatus = typeof positionStatuses[number];
export type CFDBalance = {
instrument: string,
balance: string,
profitLoss: string,
fundingRate: string,
equity: string,
position: string,
currentPrice: string,
positionPrice: string,
reserves: string,
margin: string,
marginUSD: string,
freeMarginUSD: string,
availableWithdrawBalance: string,
maxAvailableLong: string,
maxAvailableShort: string,
leverage: string,
status: PositionStatus,
export interface CFDBalance {
instrument: string
balance: string
profitLoss: string
fundingRate: string
equity: string
position: string
currentPrice: string
positionPrice: string
reserves: string
margin: string
marginUSD: string
freeMarginUSD: string
availableWithdrawBalance: string
maxAvailableLong: string
maxAvailableShort: string
leverage: string
status: PositionStatus
}
export interface Order {
senderAddress: string; // address
matcherAddress: string; // address
baseAsset: string; // address
quoteAsset: string; // address
matcherFeeAsset: string; // address
amount: number; // uint64
price: number; // uint64
matcherFee: number; // uint64
nonce: number; // uint64
expiration: number; // uint64
buySide: number; // uint8, 1=buy, 0=sell
isPersonalSign: boolean; // bool
senderAddress: string // address
matcherAddress: string // address
baseAsset: string // address
quoteAsset: string // address
matcherFeeAsset: string // address
amount: number // uint64
price: number // uint64
matcherFee: number // uint64
nonce: number // uint64
expiration: number // uint64
buySide: 0 | 1 // uint8, 1=buy, 0=sell
isPersonalSign: boolean // bool
}
export interface CFDOrder {
senderAddress: string; // address
matcherAddress: string; // address
instrumentAddress: string; // address
amount: number; // uint64
price: number; // uint64
matcherFee: number; // uint64
nonce: number; // uint64
expiration: number; // uint64
buySide: number; // uint8, 1=buy, 0=sell
isPersonalSign: boolean; // bool
senderAddress: string // address
matcherAddress: string // address
instrumentAddress: string // address
amount: number // uint64
price: number // uint64
matcherFee: number // uint64
nonce: number // uint64
expiration: number // uint64
buySide: 0 | 1 // uint8, 1=buy, 0=sell
isPersonalSign: boolean // bool
}
export interface SignedCFDOrder extends CFDOrder {
id: string; // hash of Order (it's not part of order structure in smart-contract)
signature: string; // bytes
id: string // hash of Order (it's not part of order structure in smart-contract)
signature: string // bytes
}
export interface SignedOrder extends Order {
id: string; // hash of Order (it's not part of order structure in smart-contract)
signature: string; // bytes
needWithdraw?: boolean; // bool (not supported yet by smart-contract)
id: string // hash of Order (it's not part of order structure in smart-contract)
signature: string // bytes
needWithdraw?: boolean // bool (not supported yet by smart-contract)
}
export interface CancelOrderRequest {
id: number | string;
senderAddress: string;
isPersonalSign: boolean;
id: number | string
senderAddress: string
isPersonalSign: boolean
}
export interface SignedCancelOrderRequest extends CancelOrderRequest {
id: number | string;
senderAddress: string;
signature: string;
id: number | string
senderAddress: string
signature: string
}
export interface Pair {
name: string;
baseCurrency: string;
quoteCurrency: string;
lastPrice: string;
openPrice: string;
change24h: string;
high: string;
low: string;
vol24h: string;
name: string
baseCurrency: string
quoteCurrency: string
lastPrice: string
openPrice: string
change24h: string
high: string
low: string
vol24h: string
}
export enum SupportedChainId {
@@ -136,102 +136,102 @@ export enum SupportedChainId {
const balanceTypes = ['exchange', 'wallet'] as const;
export type Source = typeof balanceTypes[number];
export type Asset = {
name: string;
address: string;
export interface Asset {
name: string
address: string
}
export type BalanceRequirement = {
readonly reason: string,
readonly asset: Asset,
readonly amount: string,
readonly sources: Source[],
readonly spenderAddress?: string;
export interface BalanceRequirement {
readonly reason: string
readonly asset: Asset
readonly amount: string
readonly sources: Source[]
readonly spenderAddress?: string
}
export type AggregatedBalanceRequirement = {
readonly asset: Asset,
readonly sources: Source[],
readonly spenderAddress?: string;
items: Partial<Record<string, string>>,
export interface AggregatedBalanceRequirement {
readonly asset: Asset
readonly sources: Source[]
readonly spenderAddress?: string
items: Partial<Record<string, string>>
}
export type ApproveFix = {
readonly type: 'byApprove',
readonly targetAmount: BigNumber.Value,
export interface ApproveFix {
readonly type: 'byApprove'
readonly targetAmount: BigNumber.Value
readonly spenderAddress: string
}
export type DepositFix = {
readonly type: 'byDeposit',
readonly amount: BigNumber.Value,
export interface DepositFix {
readonly type: 'byDeposit'
readonly amount: BigNumber.Value
readonly asset: string
}
type Fix = ApproveFix | DepositFix;
export type BalanceIssue = {
readonly asset: Asset,
readonly message: string;
readonly sources: Source[],
readonly fixes?: Fix[],
export interface BalanceIssue {
readonly asset: Asset
readonly message: string
readonly sources: Source[]
readonly fixes?: Fix[]
}
export type Exchange = typeof exchanges[number];
export type OrderbookItem = {
price: string,
amount: string,
exchanges: Exchange[],
vob: {
side: 'BUY' | 'SELL',
export interface OrderbookItem {
price: string
amount: string
exchanges: Exchange[]
vob: Array<{
side: 'BUY' | 'SELL'
pairName: string
}[]
}>
}
export type SwapInfoAlternative = {
exchanges: Exchange[],
path: string[],
marketAmountOut?: number,
marketAmountIn?: number,
marketPrice: number,
availableAmountIn?: number,
availableAmountOut?: number,
export interface SwapInfoAlternative {
exchanges: Exchange[]
path: string[]
marketAmountOut?: number
marketAmountIn?: number
marketPrice: number
availableAmountIn?: number
availableAmountOut?: number
}
export type SwapInfoBase = {
swapRequestId: string,
assetIn: string,
assetOut: string,
amountIn: number,
amountOut: number,
minAmountIn: number,
minAmountOut: number,
export interface SwapInfoBase {
swapRequestId: string
assetIn: string
assetOut: string
amountIn: number
amountOut: number
minAmountIn: number
minAmountOut: number
path: string[],
exchanges?: Exchange[],
poolOptimal: boolean,
path: string[]
exchanges?: Exchange[]
poolOptimal: boolean
price?: number,
marketPrice?: number,
price?: number
marketPrice?: number
orderInfo?: {
pair: string,
side: 'BUY' | 'SELL',
amount: number,
safePrice: number,
},
alternatives: SwapInfoAlternative[],
pair: string
side: 'BUY' | 'SELL'
amount: number
safePrice: number
}
alternatives: SwapInfoAlternative[]
}
export type SwapInfoByAmountIn = SwapInfoBase & {
kind: 'exactSpend',
availableAmountIn?: number,
marketAmountOut?: number,
kind: 'exactSpend'
availableAmountIn?: number
marketAmountOut?: number
}
export type SwapInfoByAmountOut = SwapInfoBase & {
kind: 'exactReceive',
marketAmountIn?: number,
availableAmountOut?: number,
kind: 'exactReceive'
marketAmountIn?: number
availableAmountOut?: number
}
export type SwapInfo = SwapInfoByAmountIn | SwapInfoByAmountOut;
@@ -243,33 +243,33 @@ export enum HistoryTransactionStatus {
CANCELLED = 'Cancelled',
}
export type VerboseOrionUnitConfig = {
export interface VerboseOrionUnitConfig {
// env?: string;
// api: string;
chainId: SupportedChainId;
nodeJsonRpc: string;
chainId: SupportedChainId
nodeJsonRpc: string
services: {
orionBlockchain: {
http: string;
http: string
// For example:
// http://localhost:3001/,
// http://10.123.34.23:3001/,
// https://blockchain.orionprotocol.io/
},
}
orionAggregator: {
http: string;
ws: string;
http: string
ws: string
// For example:
// http://localhost:3002/,
// http://10.34.23.5:3002/,
// shttps://aggregator.orionprotocol.io/
},
}
priceFeed: {
api: string;
api: string
// For example:
// http://localhost:3003/,
// http://10.23.5.11:3003/,
// https://price-feed.orionprotocol.io/
},
}
}
};
}

View File

@@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { type ethers } from 'ethers';
/**
* Converts normalized blockchain ("machine-readable") number to denormalized ("human-readable") number.
@@ -9,6 +9,6 @@ import { ethers } from 'ethers';
*/
export default function denormalizeNumber(input: ethers.BigNumber, decimals: BigNumber.Value) {
const decimalsBN = new BigNumber(decimals);
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimals.toString()}' is not an integer`);
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimalsBN.toString()}' is not an integer`);
return new BigNumber(input.toString()).div(new BigNumber(10).pow(decimalsBN));
}

View File

@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { Source } from '../types';
import { type Source } from '../types';
export default function getAvailableFundsSources(
expenseType: 'amount' | 'network_fee' | 'orion_fee',

View File

@@ -1,10 +1,10 @@
import { ERC20__factory, Exchange } from '@orionprotocol/contracts';
import { ERC20__factory, type Exchange } from '@orionprotocol/contracts';
import BigNumber from 'bignumber.js';
import type BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { utils } from '..';
import { INTERNAL_ORION_PRECISION, NATIVE_CURRENCY_PRECISION } from '../constants';
import { OrionAggregator } from '../services/OrionAggregator';
import { type OrionAggregator } from '../services/OrionAggregator';
export default async function getBalance(
orionAggregator: OrionAggregator,

View File

@@ -1,7 +1,7 @@
import { Exchange } from '@orionprotocol/contracts';
import BigNumber from 'bignumber.js';
import { ethers } from 'ethers';
import { OrionAggregator } from '../services/OrionAggregator';
import { type Exchange } from '@orionprotocol/contracts';
import type BigNumber from 'bignumber.js';
import { type ethers } from 'ethers';
import { type OrionAggregator } from '../services/OrionAggregator';
import getBalance from './getBalance';
export default async (
@@ -14,7 +14,7 @@ export default async (
const balances = await Promise.all(
Object.entries(balancesRequired)
.map(async ([asset, assetAddress]) => {
if (!assetAddress) throw new Error(`Asset address of ${asset} not found`);
if (assetAddress === undefined) throw new Error(`Asset address of ${asset} not found`);
const balance = await getBalance(
orionAggregator,
asset,
@@ -31,8 +31,8 @@ export default async (
);
return balances.reduce<Partial<Record<string, {
exchange: BigNumber,
wallet: BigNumber,
exchange: BigNumber
wallet: BigNumber
}>>>((prev, curr) => ({
...prev,
[curr.asset]: curr.amount,

View File

@@ -4,15 +4,15 @@ const getNativeCryptocurrency = (assetToAddress: Partial<Record<string, string>>
const addressToAsset = Object
.entries(assetToAddress)
.reduce<Partial<Record<string, string>>>((prev, [asset, address]) => {
if (!address) return prev;
return {
...prev,
[address]: asset,
};
}, {});
if (address === undefined) return prev;
return {
...prev,
[address]: asset,
};
}, {});
const nativeCryptocurrency = addressToAsset[ethers.constants.AddressZero];
if (!nativeCryptocurrency) throw new Error('Native cryptocurrency asset is not found');
if (nativeCryptocurrency === undefined) throw new Error('Native cryptocurrency asset is not found');
return nativeCryptocurrency;
};

View File

@@ -1,14 +1,14 @@
export default class HttpError extends Error {
public code: number;
public errorMessage: string |null;
public errorMessage: string | null;
public statusText: string;
public type: string;
constructor(code:number, message:string|null, type: string, statusText:string) {
super(message || '');
constructor(code: number, message: string | null, type: string, statusText: string) {
super(message ?? '');
this.errorMessage = message;
this.type = type;
this.statusText = statusText;

View File

@@ -14,7 +14,7 @@ export default function normalizeNumber(
roundingMode: BigNumber.RoundingMode,
) {
const decimalsBN = new BigNumber(decimals);
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimals.toString()}' is not an integer`);
if (!decimalsBN.isInteger()) throw new Error(`Decimals '${decimalsBN.toString()}' is not an integer`);
const inputBN = new BigNumber(input);
return ethers.BigNumber.from(
inputBN

View File

@@ -1,21 +1,21 @@
type WithReason = {
reason: string;
interface WithReason {
reason: string
}
type WithCodeError = Error & {
code: number | string;
code: number | string
}
type WithMessage = {
message: string;
interface WithMessage {
message: string
}
type WithDataError = Error & {
data: Record<string, unknown>;
data: Record<string, unknown>
}
type WithError ={
error: Record<string | number | symbol, unknown>;
interface WithError {
error: Record<string | number | symbol, unknown>
}
export const makePartial = <Key extends string | number | symbol, Value>(value: Record<Key, Value>): Partial<Record<Key, Value>> => value;