removed crypto-js dependency

This commit is contained in:
KS
2024-04-04 17:55:26 +03:00
18 changed files with 212 additions and 119 deletions

View File

@@ -21,10 +21,15 @@ export default class Orion {
// TODO: get tradable pairs (aggregated)
public logger: ((message: string) => void) | undefined;
constructor(
envOrConfig: KnownEnv | EnvConfig = 'production',
overrides?: DeepPartial<EnvConfig>
overrides?: DeepPartial<EnvConfig>,
logger?: ((message: string) => void) | undefined
) {
this.logger = logger;
let config: EnvConfig;
if (typeof envOrConfig === 'string') {
const envConfig = envs[envOrConfig];
@@ -33,7 +38,7 @@ export default class Orion {
}
this.env = envOrConfig;
config = {
analyticsAPI: envConfig.analyticsAPI,
analyticsAPI: envConfig?.analyticsAPI,
referralAPI: envConfig.referralAPI,
networks: Object.entries(envConfig.networks).map(([chainId, networkConfig]) => {
if (!isValidChainId(chainId)) throw new Error(`Invalid chainId: ${chainId}`);
@@ -92,7 +97,7 @@ export default class Orion {
// api: networkConfig.api,
nodeJsonRpc: networkConfig.nodeJsonRpc,
services: networkConfig.services,
});
}, logger);
return {
...acc,
[chainId]: unit,

View File

@@ -397,7 +397,7 @@ async function shouldUseExchangeBalance(
let useExchangeBalance = true;
let additionalTransferAmount = 0n;
if (exchangeBalance == 0n) {
if (walletBalance >= amount || exchangeBalance == 0n) {
useExchangeBalance = false;
additionalTransferAmount = amount;
} else {

View File

@@ -41,7 +41,10 @@ export default class Unit {
public readonly contracts: Record<string, string>;
constructor(config: KnownConfig | VerboseUnitConfig) {
public logger: ((message: string) => void) | undefined;
constructor(config: KnownConfig | VerboseUnitConfig, logger?: ((message: string) => void) | undefined) {
this.logger = logger;
if ('env' in config) {
const staticConfig = envs[config.env];
if (!staticConfig) {
@@ -118,7 +121,8 @@ export default class Unit {
this.aggregator = new Aggregator(
this.config.services.aggregator.http,
this.config.services.aggregator.ws,
this.config.basicAuth
this.config.basicAuth,
logger,
);
this.priceFeed = new PriceFeed(
this.config.services.priceFeed.api,

View File

@@ -219,5 +219,18 @@
"WETH": "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f",
"curveRegistry": ""
}
},
"43114": {
"chainId": "43114",
"label": "Avalanche Network",
"shortName": "Avax",
"code": "avax",
"baseCurrencyName": "AVAX",
"rpc": "https://api.avax.network/ext/bc/C/rpc/",
"explorer": "https://snowtrace.io/",
"contracts": {
"WETH": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
"curveRegistry": ""
}
}
}

View File

@@ -1,6 +1,5 @@
{
"production": {
"analyticsAPI": "https://trade.orion.xyz/api/stats",
"referralAPI": "https://trade.orion.xyz/referral-api",
"networks": {
"1": {
@@ -165,11 +164,28 @@
"http": "/orion-indexer/"
}
}
},
"43114": {
"api": "https://trade.orion.xyz/avalanche-c-chain",
"services": {
"aggregator": {
"http": "/backend",
"ws": "/v1"
},
"blockchain": {
"http": ""
},
"priceFeed": {
"all": "/price-feed"
},
"indexer": {
"http": "/orion-indexer/"
}
}
}
}
},
"testing": {
"analyticsAPI": "https://trade.orion.xyz/api/stats",
"referralAPI": "https://testing.orion.xyz/referral-api",
"networks": {
"97": {
@@ -284,7 +300,6 @@
}
},
"staging": {
"analyticsAPI": "https://trade.orion.xyz/api/stats",
"referralAPI": "https://staging.orion.xyz/referral-api",
"networks": {
"1": {
@@ -448,11 +463,28 @@
"http": "/orion-indexer/"
}
}
},
"43114": {
"api": "https://trade.orion.xyz/avalanche-c-chain",
"services": {
"aggregator": {
"http": "/backend",
"ws": "/v1"
},
"blockchain": {
"http": ""
},
"priceFeed": {
"all": "/price-feed"
},
"indexer": {
"http": "/orion-indexer/"
}
}
}
}
},
"experimental": {
"analyticsAPI": "https://trade.orion.xyz/api/stats",
"referralAPI": "https://testing.orion.xyz/referral-api",
"networks": {
"97": {
@@ -494,7 +526,6 @@
}
},
"kucoin-production": {
"analyticsAPI": "https://trade.orion.xyz/api/stats",
"referralAPI": "https://trade.orion.xyz/referral-api",
"networks": {
"1": {

View File

@@ -23,7 +23,7 @@ export const pureEnvNetworksSchema = z.object({
});
export const pureEnvPayloadSchema = z.object({
analyticsAPI: z.string().url(),
analyticsAPI: z.string().url().optional(),
referralAPI: z.string().url(),
networks: z.record(
z.nativeEnum(SupportedChainId),

View File

@@ -19,4 +19,5 @@ export const productionChains = [
SupportedChainId.OPBNB,
SupportedChainId.INEVM,
SupportedChainId.LINEA,
SupportedChainId.AVAX,
];

View File

@@ -1 +1 @@
export default ['ftm', 'bsc', 'eth', 'polygon', 'okc', 'arb', 'drip', 'opbnb', 'inevm', 'linea'] as const;
export default ['ftm', 'bsc', 'eth', 'polygon', 'okc', 'arb', 'drip', 'opbnb', 'inevm', 'linea', 'avax'] as const;

View File

@@ -1 +1 @@
export default ['FTM', 'BSC', 'ETH', 'POLYGON', 'OKC', 'ARB', 'OPBNB', 'INEVM', 'LINEA'] as const;
export default ['FTM', 'BSC', 'ETH', 'POLYGON', 'OKC', 'ARB', 'OPBNB', 'INEVM', 'LINEA', 'AVAX'] as const;

View File

@@ -35,11 +35,16 @@ class Aggregator {
return this.apiUrl;
}
public logger: ((message: string) => void) | undefined;
constructor(
httpAPIUrl: string,
wsAPIUrl: string,
basicAuth?: BasicAuthCredentials
basicAuth?: BasicAuthCredentials,
logger?: ((message: string) => void) | undefined
) {
this.logger = logger;
// const oaUrl = new URL(apiUrl);
// const oaWsProtocol = oaUrl.protocol === 'https:' ? 'wss' : 'ws';
// const aggregatorWsUrl = `${oaWsProtocol}://${oaUrl.host + (oaUrl.pathname === '/'
@@ -47,7 +52,7 @@ class Aggregator {
// : oaUrl.pathname)}/v1`;
this.apiUrl = httpAPIUrl;
this.ws = new AggregatorWS(httpToWS(wsAPIUrl));
this.ws = new AggregatorWS(httpToWS(wsAPIUrl), undefined, logger);
this.basicAuth = basicAuth;
this.getHistoryAtomicSwaps = this.getHistoryAtomicSwaps.bind(this);
@@ -374,8 +379,7 @@ class Aggregator {
return fetchWithValidation(url.toString(), atomicSwapHistorySchema, { headers: this.basicAuthHeaders });
};
private encode_utf8(s : string) {
private encode_utf8(s: string) {
return unescape(encodeURIComponent(s));
}
@@ -385,12 +389,12 @@ class Aggregator {
.digest('hex');
}
private generateHeaders(body : any, method : string, path : string, timestamp : number, apiKey : string, secretKey : string) {
private generateHeaders(body: any, method: string, path: string, timestamp: number, apiKey: string, secretKey: string) {
const sortedBody = Object.keys(body)
.sort()
.map((key) => (
`${key}=${body[key]}`
)).join('&');
.sort()
.map((key) => (
`${key}=${body[key]}`
)).join('&');
const payload = timestamp + method.toUpperCase() + path + sortedBody;
@@ -407,40 +411,38 @@ class Aggregator {
}
public async RFQOrder(
tokenFrom: string,
tokenTo: string,
fromTokenAmount: string,
apiKey: string, //
secretKey: string,
wallet: string
) : Promise<z.infer<typeof pmmOrderSchema>> {
tokenFrom: string,
tokenTo: string,
fromTokenAmount: string,
apiKey: string, //
secretKey: string,
wallet: string
): Promise<z.infer<typeof pmmOrderSchema>> {
// Making the order structure
const
path = '/rfq'
, url = `${this.apiUrl}/api/v1/integration/pmm`+path
, headers = {
'Content-Type': 'application/json',
}
, data = {
"baseToken":tokenFrom, // USDT
"quoteToken":tokenTo, // ORN
"amount": fromTokenAmount, // 100
"taker": wallet,
"feeBps": 0
}
, method = 'POST'
, timestamp = Date.now()
, signatureHeaders = this.generateHeaders(data, method, path, timestamp, apiKey, secretKey)
, compiledHeaders = {...headers, ...signatureHeaders.headers, }
, body = JSON.stringify(data)
path = '/rfq';
const url = `${this.apiUrl}/api/v1/integration/pmm` + path;
const headers = {
'Content-Type': 'application/json',
};
const data = {
baseToken: tokenFrom, // USDT
quoteToken: tokenTo, // ORN
amount: fromTokenAmount, // 100
taker: wallet,
feeBps: 0
};
const method = 'POST';
const timestamp = Date.now();
const signatureHeaders = this.generateHeaders(data, method, path, timestamp, apiKey, secretKey);
const compiledHeaders = { ...headers, ...signatureHeaders.headers, };
const body = JSON.stringify(data)
;
let res = pmmOrderSchema.parse({});
const res = pmmOrderSchema.parse({});
try {
const result = await fetch(url,{
const result = await fetch(url, {
headers: compiledHeaders,
method,
body
@@ -449,14 +451,13 @@ class Aggregator {
const json = await result.json();
const parseResult = pmmOrderSchema.safeParse(json);
if(!parseResult.success) {
if (!parseResult.success) {
// Try to parse error answer
const errorSchema = z.object({error: z.object({code: z.number(), reason: z.string()})});
const errorSchema = z.object({ error: z.object({ code: z.number(), reason: z.string() }) });
const errorParseResult = errorSchema.safeParse(json);
if(!errorParseResult.success)
throw Error(`Unrecognized answer from aggregator: ${json}`);
if (!errorParseResult.success) { throw Error(`Unrecognized answer from aggregator: ${json}`); }
throw Error(errorParseResult.data.error.reason);
}
@@ -465,8 +466,8 @@ class Aggregator {
res.signature = parseResult.data.signature;
res.error = '';
res.success = true;
}
catch(err) {
// return result;
} catch (err) {
res.error = `${err}`;
}
return res;

View File

@@ -66,10 +66,11 @@ type PairConfigSubscription = {
type AggregatedOrderbookSubscription = {
payload: string
dc?: number
callback: (
asks: OrderbookItem[],
bids: OrderbookItem[],
pair: string
pair: string,
) => void
errorCb?: (message: string) => void
}
@@ -195,9 +196,10 @@ class AggregatorWS {
readonly basicAuth?: BasicAuthCredentials | undefined;
constructor(wsUrl: string, basicAuth?: BasicAuthCredentials) {
constructor(wsUrl: string, basicAuth?: BasicAuthCredentials, logger?: ((message: string) => void) | undefined) {
this.wsUrl = wsUrl;
this.basicAuth = basicAuth;
this.logger = logger;
}
private messageQueue: BufferLike[] = [];
@@ -252,7 +254,7 @@ class AggregatorWS {
subscription: Subscription[T],
prevSubscriptionId?: string
) {
const id = type === 'aobus'
const id = type === SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE
? ((subscription as any).payload as string) // TODO: Refactor!!!
: uuidv4();
@@ -262,6 +264,12 @@ class AggregatorWS {
subRequest['T'] = type;
subRequest['id'] = id;
if ('dc' in subscription) {
if (typeof subscription.dc === 'number') {
subRequest['dc'] = subscription.dc;
}
}
if ('payload' in subscription) {
if (typeof subscription.payload === 'string') {
subRequest['S'] = subscription.payload;
@@ -369,11 +377,11 @@ class AggregatorWS {
delete this.subscriptions[SubscriptionType.ASSET_PAIR_CONFIG_UPDATES_SUBSCRIBE]?.[newestSubId];
// !!! swap info subscription is uuid that contains hyphen
} else if (isOrderBooksSubscription(newestSubId)) { // is pair name(AGGREGATED_ORDER_BOOK_UPDATE)
const aobSubscriptions = this.subscriptions[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE];
if (aobSubscriptions) {
const targetAobSub = Object.entries(aobSubscriptions).find(([, value]) => value?.payload === newestSubId);
if (targetAobSub) {
const [key] = targetAobSub;
const aobusSubscriptions = this.subscriptions[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE];
if (aobusSubscriptions) {
const targetAobusSub = Object.entries(aobusSubscriptions).find(([, value]) => value?.payload === newestSubId);
if (targetAobusSub) {
const [key] = targetAobusSub;
delete this.subscriptions[SubscriptionType.AGGREGATED_ORDER_BOOK_UPDATES_SUBSCRIBE]?.[key];
}
}

View File

@@ -114,6 +114,7 @@ class BlockchainService {
this.getRedeemOrderBySecretHash = this.getRedeemOrderBySecretHash.bind(this);
this.claimOrder = this.claimOrder.bind(this);
this.getGasLimits = this.getGasLimits.bind(this);
this.getExchangeContractWalletBalance = this.getExchangeContractWalletBalance.bind(this);
}
get basicAuthHeaders() {
@@ -495,6 +496,12 @@ class BlockchainService {
z.record(z.number()),
{ headers: this.basicAuthHeaders }
);
getExchangeContractWalletBalance = (exchangeContractAddress: string) => fetchWithValidation(
`${this.apiUrl}/api/broker/getWalletBalance/${exchangeContractAddress}`,
z.record(z.string()),
{ headers: this.basicAuthHeaders }
);
}
export * as schemas from './schemas/index.js';

View File

@@ -9,7 +9,7 @@ import {
PoolV2InfoResponseSchema,
testIncrementorSchema,
veORNInfoResponseSchema,
votingInfoResponseSchema
votingInfoResponseSchema,
} from './schemas';
import { fetchWithValidation } from 'simple-typed-fetch';
import { BigNumber } from 'bignumber.js';
@@ -94,6 +94,7 @@ class IndexerService {
this.veORNInfo = this.veORNInfo.bind(this);
this.listAmount = this.listAmount.bind(this);
this.getAmountByORN = this.getAmountByORN.bind(this);
this.getAmountAt = this.getAmountAt.bind(this);
this.getAmountAtCurrent = this.getAmountAtCurrent.bind(this);
this.getVotingInfo = this.getVotingInfo.bind(this);
}
@@ -117,6 +118,23 @@ class IndexerService {
});
};
/**
* @param {number} amount - amount
* @param {number} [timestamp = Date.now()] - timestamp, defaults to current time
*/
readonly getAmountAt = (
amount: number,
timestamp = Date.now()
): BigNumber => {
const finalTimestamp = timestamp / 1000;
// sqrt
return BigNumber(amount).dividedBy(this.getK(finalTimestamp));
};
/**
* @deprecated since version 69 in favor of getAmountAt
*/
readonly getAmountAtCurrent = (amount: number): BigNumber => {
const timestamp = Date.now() / 1000;
@@ -134,8 +152,7 @@ class IndexerService {
const multSQRT = deltaDaysBN.dividedBy(WEEK_DAYS).sqrt();
const multCUBE = deltaDaysBN.dividedBy(alpha).pow(3);
return BigNumber(amountToken)
.multipliedBy(multSQRT.plus(multCUBE));
return BigNumber(amountToken).multipliedBy(multSQRT.plus(multCUBE));
};
readonly getVotingInfo = (userAddress?: string) => {
@@ -208,7 +225,11 @@ class IndexerService {
});
};
readonly poolV2Info = (token0: string, token1: string, address: string | undefined) => {
readonly poolV2Info = (
token0: string,
token1: string,
address: string | undefined
) => {
return fetchWithValidation(this.apiUrl, PoolV2InfoResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({

View File

@@ -90,6 +90,7 @@ export enum SupportedChainId {
OPBNB = '204',
INEVM = '2525',
LINEA = '59144',
AVAX = '43114',
POLYGON_TESTNET = '80001',
FANTOM_TESTNET = '4002',
@@ -279,7 +280,7 @@ export type KnownEnv = typeof knownEnvs[number];
export type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
export type EnvConfig = {
analyticsAPI: string
analyticsAPI: string | undefined
referralAPI: string
networks: Partial<
Record<