Merged with main

This commit is contained in:
lomonoshka
2023-10-10 19:03:38 +04:00
70 changed files with 9828 additions and 753 deletions

View File

@@ -74,13 +74,13 @@ class Aggregator {
}
getOrder = (orderId: string, owner?: string) => {
if (!ethers.utils.isHexString(orderId)) {
if (!ethers.isHexString(orderId)) {
throw new Error(`Invalid order id: ${orderId}. Must be a hex string`);
}
const url = new URL(`${this.apiUrl}/api/v1/order`);
url.searchParams.append('orderId', orderId);
if (owner !== undefined) {
if (!ethers.utils.isAddress(owner)) {
if (!ethers.isAddress(owner)) {
throw new Error(`Invalid owner address: ${owner}`);
}
url.searchParams.append('owner', owner);

View File

@@ -3,22 +3,22 @@ import { z } from 'zod';
import { exchanges, orderStatuses, subOrderStatuses } from '../../../constants/index.js';
const blockchainOrderSchema = z.object({
id: z.string().refine(ethers.utils.isHexString, (value) => ({
id: z.string().refine(ethers.isHexString, (value) => ({
message: `blockchainOrder.id must be a hex string, got ${value}`,
})),
senderAddress: z.string().refine(ethers.utils.isAddress, (value) => ({
senderAddress: z.string().refine(ethers.isAddress, (value) => ({
message: `blockchainOrder.senderAddress must be an address, got ${value}`,
})),
matcherAddress: z.string().refine(ethers.utils.isAddress, (value) => ({
matcherAddress: z.string().refine(ethers.isAddress, (value) => ({
message: `blockchainOrder.matcherAddress must be an address, got ${value}`,
})),
baseAsset: z.string().refine(ethers.utils.isAddress, (value) => ({
baseAsset: z.string().refine(ethers.isAddress, (value) => ({
message: `blockchainOrder.baseAsset must be an address, got ${value}`,
})),
quoteAsset: z.string().refine(ethers.utils.isAddress, (value) => ({
quoteAsset: z.string().refine(ethers.isAddress, (value) => ({
message: `blockchainOrder.quoteAsset must be an address, got ${value}`,
})),
matcherFeeAsset: z.string().refine(ethers.utils.isAddress, (value) => ({
matcherFeeAsset: z.string().refine(ethers.isAddress, (value) => ({
message: `blockchainOrder.matcherFeeAsset must be an address, got ${value}`,
})),
amount: z.number().int().nonnegative(),
@@ -27,7 +27,7 @@ const blockchainOrderSchema = z.object({
nonce: z.number(),
expiration: z.number(),
buySide: z.union([z.literal(1), z.literal(0)]),
signature: z.string().refine(ethers.utils.isHexString, (value) => ({
signature: z.string().refine(ethers.isHexString, (value) => ({
message: `blockchainOrder.signature must be a hex string, got ${value}`,
})).nullable(),
isPersonalSign: z.boolean(),
@@ -53,7 +53,7 @@ const baseOrderSchema = z.object({
amount: z.number().nonnegative(),
remainingAmount: z.number().nonnegative(),
price: z.number().nonnegative(),
sender: z.string().refine(ethers.utils.isAddress, (value) => ({
sender: z.string().refine(ethers.isAddress, (value) => ({
message: `order.sender must be an address, got ${value}`,
})),
filledAmount: z.number().nonnegative(),
@@ -77,13 +77,13 @@ const brokerAddressSchema = z.enum([
'SELF_BROKER'
])
.or(selfBrokerSchema)
.or(z.string().refine(ethers.utils.isAddress, (value) => ({
.or(z.string().refine(ethers.isAddress, (value) => ({
message: `subOrder.subOrders.[n].brokerAddress must be an address, got ${value}`,
})));
const subOrderSchema = baseOrderSchema.extend({
price: z.number(),
id: z.number(),
parentOrderId: z.string().refine(ethers.utils.isHexString, (value) => ({
parentOrderId: z.string().refine(ethers.isHexString, (value) => ({
message: `subOrder.parentOrderId must be a hex string, got ${value}`,
})),
exchange: z.string(),
@@ -98,11 +98,11 @@ const subOrderSchema = baseOrderSchema.extend({
});
const orderSchema = z.object({
orderId: z.string().refine(ethers.utils.isHexString, (value) => ({
orderId: z.string().refine(ethers.isHexString, (value) => ({
message: `orderId must be a hex string, got ${value}`,
})),
order: baseOrderSchema.extend({
id: z.string().refine(ethers.utils.isHexString, (value) => ({
id: z.string().refine(ethers.isHexString, (value) => ({
message: `order.id must be a hex string, got ${value}`,
})),
fee: z.number().nonnegative(),

View File

@@ -108,7 +108,7 @@ export const fullOrderSchema = z.object({
subOrders: o.c.map((so) => ({
pair: so.P,
exchange: so.e,
exchangs: so.es,
exchanges: so.es,
id: so.i,
amount: so.a,
settledAmount: so.A,

View File

@@ -59,6 +59,13 @@ type AtomicSwapHistoryTargetQuery = AtomicSwapHistoryBaseQuery & {
expiredRedeem?: 0 | 1
state?: 'REDEEMED' | 'BEFORE-REDEEM'
}
type PlatformFees = {
assetIn?: string // TODO: return types from main branch
assetOut?: string
walletAddress?: string | undefined
fromWidget?: string | undefined
}
class BlockchainService {
private readonly apiUrl: string;
@@ -231,17 +238,16 @@ class BlockchainService {
{ headers: this.basicAuthHeaders }
);
getPlatformFees = (
{ assetIn, assetOut, walletAddress, fromWidget }: {
assetIn?: string | undefined,
assetOut?: string | undefined,
walletAddress?: string | undefined,
fromWidget?: string | undefined
}
getPlatformFees = ({
assetIn,
assetOut,
walletAddress,
fromWidget
}: PlatformFees
) => {
const url = new URL(`${this.apiUrl}/api/platform-fees`);
if (assetIn !== undefined) {
if (assetIn !== undefined) { // TODO: make same as in main branch
url.searchParams.append('assetIn', assetIn);
}

View File

@@ -16,9 +16,9 @@ const baseAtomicHistoryItem = z.object({
_id: z.string(),
__v: z.number(),
asset: z.string(),
sender: z.string().refine(ethers.utils.isAddress),
secretHash: z.string().refine(ethers.utils.isHexString),
receiver: z.string().refine(ethers.utils.isAddress).optional(),
sender: z.string().refine(ethers.isAddress),
secretHash: z.string().refine(ethers.isHexString),
receiver: z.string().refine(ethers.isAddress).optional(),
secret: z.string().optional(),
});

View File

@@ -0,0 +1,3 @@
export const AVAILABLE_POOL_FEE = ['0.01', '0.05', '0.3', '1'] as const;
export const INITIAL_VEORN_ADJUSTMENT_FACTOR = 5;
export const LOCK_START_TIME = 1690848000;// Aug 01 2023 00:00:00 UTC

View File

@@ -0,0 +1,225 @@
import {
environmentResponseSchema,
getPoolResponseSchema,
listAmountResponseSchema,
listNFTOrderResponseSchema,
listPoolResponseSchema,
testIncrementorSchema,
veORNInfoResponseSchema,
votingInfoResponseSchema,
} from './schemas/index.js';
import { fetchWithValidation } from 'simple-typed-fetch';
import { BigNumber } from 'bignumber.js';
import { DAY, WEEK_DAYS, YEAR } from '../../constants/index.js';
import { LOCK_START_TIME } from './constants.js';
type BasePayload = {
chainId: number
jsonrpc: '1.0'
};
type GetEnvironmentPayload = BasePayload & {
model: 'Environment'
method: 'getEnvironment'
params: []
};
type ListNFTOrderPayload = BasePayload & {
model: 'OrionV3NFTManager'
method: 'listNFTOrder'
params: [string]
};
type GetPoolInfoPayload = BasePayload & {
model: 'OrionV3Factory'
method: 'getPoolInfo'
params: [string, string, string]
};
type ListPoolPayload = BasePayload & {
model: 'OrionFarmV3'
method: 'listPool'
params: [string]
};
type VeORNInfoPayload = BasePayload & {
model: 'veORN'
method: 'info'
params: [string]
};
type ListAmountPayload = BasePayload & {
model: string
method: 'listAmount'
params: []
};
type GetAmountByORNPayload = BasePayload & {
amountToken: number
timeLock: number
};
type Payload =
| GetEnvironmentPayload
| ListNFTOrderPayload
| GetPoolInfoPayload
| ListPoolPayload
| VeORNInfoPayload
| ListAmountPayload
| GetAmountByORNPayload;
class IntegratorService {
private readonly apiUrl: string;
private readonly chainId: number;
get api() {
return this.apiUrl;
}
constructor(apiUrl: string, chainId: number) {
this.apiUrl = apiUrl;
this.chainId = chainId;
this.getEnvironment = this.getEnvironment.bind(this);
this.listNFTOrder = this.listNFTOrder.bind(this);
this.getPoolInfo = this.getPoolInfo.bind(this);
this.listPool = this.listPool.bind(this);
this.veORNInfo = this.veORNInfo.bind(this);
this.listAmount = this.listAmount.bind(this);
this.getAmountByORN = this.getAmountByORN.bind(this);
this.getAmountAtCurrent = this.getAmountAtCurrent.bind(this);
this.getVotingInfo = this.getVotingInfo.bind(this);
}
readonly makeRPCPayload = (payload: Omit<Payload, 'chainId' | 'jsonrpc'>) => {
return JSON.stringify({
...payload,
chainId: this.chainId,
jsonrpc: '1.0',
});
};
readonly veORNInfo = (address?: string) => {
return fetchWithValidation(this.apiUrl, veORNInfoResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'veORN',
method: 'info',
params: [address],
}),
});
};
readonly getAmountAtCurrent = (amount: number): BigNumber => {
const timestamp = Date.now() / 1000;
// sqrt
return BigNumber(amount).dividedBy(this.getK(timestamp));
};
readonly getAmountByORN = (amountToken: number, timeLock: number) => {
const timestamp = Date.now() / 1000;
const deltaDaysBN = BigNumber(timeLock).minus(timestamp).dividedBy(DAY);
if (deltaDaysBN.lte(0)) return 0;
return BigNumber(amountToken)
.multipliedBy(deltaDaysBN.sqrt())
.dividedBy(BigNumber(WEEK_DAYS).sqrt());
};
readonly getVotingInfo = (userAddress?: string) => {
return fetchWithValidation(this.apiUrl, votingInfoResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'OrionVoting',
method: 'info',
params: [userAddress],
}),
});
};
readonly getEnvironment = () => {
return fetchWithValidation(this.apiUrl, environmentResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'Environment',
method: 'getEnvironment',
params: [],
}),
});
};
readonly listNFTOrder = (address: string) => {
return fetchWithValidation(this.apiUrl, listNFTOrderResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'OrionV3NFTManager',
method: 'listNFTOrder',
params: [address],
}),
});
};
readonly getPoolInfo = (
token0: string,
token1: string,
poolAddress: string
) => {
return fetchWithValidation(this.apiUrl, getPoolResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'OrionV3Factory',
method: 'getPoolInfo',
params: [token0, token1, poolAddress],
}),
});
};
readonly listPool = (address?: string) => {
return fetchWithValidation(this.apiUrl, listPoolResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'OrionFarmV3',
method: 'listPool',
params: [address],
}),
});
};
readonly listAmount = (poolKey: string) => {
return fetchWithValidation(this.apiUrl, listAmountResponseSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: poolKey,
method: 'listAmount',
params: [],
}),
});
};
readonly testRetrieve = () => {
return fetchWithValidation(this.apiUrl, testIncrementorSchema, {
method: 'POST',
body: this.makeRPCPayload({
model: 'Incrementer',
method: 'retrieve',
params: [],
}),
});
};
private readonly getK = (time: number) => {
const currentTime = time < LOCK_START_TIME ? LOCK_START_TIME : time;
const deltaYears = BigNumber(currentTime)
.minus(LOCK_START_TIME)
.dividedBy(YEAR);
return 2 ** BigNumber(deltaYears).multipliedBy(2).toNumber();
};
}
export * as schemas from './schemas/index.js';
export { IntegratorService };

View File

@@ -0,0 +1,26 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
const basicPoolInfo = z.object({
poolAddress: evmAddressSchema,
isInitialized: z.boolean(),
liquidity: z.number().nonnegative(),
liquidityInUsd: z.number().nonnegative(),
liquidityShare: z.number().nonnegative(),
isFarming: z.boolean(),
rewardsTotal: z.number().nonnegative(),
rewardsPerPeriod: z.number().nonnegative(),
rewardsShare: z.number().nonnegative(),
feePerPeriod: z.number().nonnegative(),
feeTotal: z.number().nonnegative(),
feeShare: z.number().nonnegative(),
tickMultiplier: z.number().nonnegative(),
MAX_TICK: z.number().nonnegative().int(),
minAPR: z.number().nonnegative(),
maxAPR: z.number().nonnegative(),
avgAPR: z.number().nonnegative(),
maxBoost: z.number().nonnegative().int(),
feeRate: z.array(z.number().nonnegative()),
});
export default basicPoolInfo;

View File

@@ -0,0 +1,19 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
import infoSchema from './info-schema.js';
const environmentResponseSchema = z.object({
result: z.object({
chainId: z.number().int().nonnegative(),
nativeToken: z.string(),
OrionV3Factory: evmAddressSchema,
OrionV3NFTManager: evmAddressSchema,
SwapRouter: evmAddressSchema,
OrionFarmV3: evmAddressSchema,
OrionVoting: evmAddressSchema,
veORN: evmAddressSchema,
}),
info: infoSchema,
});
export default environmentResponseSchema;

View File

@@ -0,0 +1,21 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
import { AVAILABLE_POOL_FEE } from '../constants.js';
import basicPoolInfo from './basic-pool-info-schema.js';
import infoSchema from './info-schema.js';
const getPoolResponseSchema = z.object({
result: z.object({
token0: z.string().nonempty(),
token1: z.string().nonempty(),
token0Address: evmAddressSchema,
token1Address: evmAddressSchema,
totalLiquidity: z.number().nonnegative(),
WETH9: evmAddressSchema,
pools: z.record(z.enum(AVAILABLE_POOL_FEE), basicPoolInfo.nullable()),
}),
info: infoSchema,
});
export default getPoolResponseSchema;

View File

@@ -0,0 +1,8 @@
export { default as environmentResponseSchema } from './environment-response-schema.js';
export { default as listNFTOrderResponseSchema } from './list-nft-order-response-schema.js';
export { default as getPoolResponseSchema } from './get-pool-response-schema.js';
export { default as listPoolResponseSchema } from './list-pool-response-schema.js';
export { default as veORNInfoResponseSchema } from './veORN-info-schema.js';
export { default as listAmountResponseSchema } from './list-amount-schema.js';
export { default as votingInfoResponseSchema } from './voting-info-schema.js';
export { default as testIncrementorSchema } from './test-incrementor-schema.js';

View File

@@ -0,0 +1,14 @@
import { z } from 'zod';
import { ethers } from 'ethers';
const infoSchema = z.object({
blockNumber: z.number().int().nonnegative(),
blockHash: z.string().refine((v) => v.length === 0 || ethers.isHexString(v), {
message: 'blockHash must be a valid hex string or empty',
}),
timeRequest: z.number().int().nonnegative(),
timeAnswer: z.number().int().nonnegative(),
changes: z.number().int().nonnegative(),
});
export default infoSchema;

View File

@@ -0,0 +1,9 @@
import { z } from 'zod';
import infoSchema from './info-schema.js';
const listAmountSchema = z.object({
result: z.record(z.number()),
info: infoSchema,
});
export default listAmountSchema;

View File

@@ -0,0 +1,10 @@
import { z } from 'zod';
import poolSchema from './pool-schema.js';
import infoSchema from './info-schema.js';
const listNFTOrderResponseSchema = z.object({
result: z.array(poolSchema),
info: infoSchema,
});
export default listNFTOrderResponseSchema;

View File

@@ -0,0 +1,26 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
import basicPoolInfo from './basic-pool-info-schema.js';
import infoSchema from './info-schema.js';
const poolOfListPoolSchema = z.object({
token0: z.string().nonempty(),
token1: z.string().nonempty(),
token0Address: evmAddressSchema,
token1Address: evmAddressSchema,
token0Decimals: z.number().int().nonnegative().max(18),
token1Decimals: z.number().int().nonnegative().max(18),
WETH9: evmAddressSchema,
...basicPoolInfo.shape,
type: z.string().nonempty(),
});
const listPoolResponseSchema = z.object({
result: z.array(poolOfListPoolSchema),
info: infoSchema,
});
export default listPoolResponseSchema;

View File

@@ -0,0 +1,33 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
const poolSchema = z.object({
tokenId: evmAddressSchema,
token0: z.string().nonempty(),
token1: z.string().nonempty(),
token0Address: evmAddressSchema,
token1Address: evmAddressSchema,
token0Decimals: z.number().int().nonnegative().max(18),
token1Decimals: z.number().int().nonnegative().max(18),
amount: z.number().nonnegative(),
amount0: z.number().nonnegative(),
amount1: z.number().nonnegative(),
from: z.number().nonnegative(),
to: z.number().nonnegative(),
fee: z.number().nonnegative(),
collectFee: z.number().nonnegative(),
reward: z.number().nonnegative(),
apr: z.number().nonnegative(),
boost: z.number().int().nonnegative(),
isStaked: z.boolean(),
poolFee: z.number().nonnegative(),
poolAddress: evmAddressSchema,
veOrnForMaxBoost: z.number().nonnegative(),
veOrnMaxBoost: z.number().nonnegative(),
veORNCurrent: z.number().nonnegative(),
time: z.number().int().nonnegative(), // tim
});
export default poolSchema;

View File

@@ -0,0 +1,9 @@
import { z } from 'zod';
import infoSchema from './info-schema.js';
const testIncrementorSchema = z.object({
result: z.number().int(),
info: infoSchema,
});
export default testIncrementorSchema;

View File

@@ -0,0 +1,14 @@
import { ethers } from 'ethers';
import { z } from 'zod';
export const evmAddressSchema = z
.string()
.refine(ethers.isAddress, (v) => ({
message: `${v} is not a valid address`,
}));
export const hexStringSchema = z
.string()
.refine(ethers.isHexString, (v) => ({
message: `${v} is not a valid hex string`,
}));

View File

@@ -0,0 +1,26 @@
import { z } from 'zod';
import { evmAddressSchema } from './util-schemas.js';
import infoSchema from './info-schema.js';
const veORNResultSchema = z.object({
avgAPR: z.number(),
minAPR: z.number(),
maxAPR: z.number(),
veTokenAddress: evmAddressSchema,
totalORNLocked: z.number(),
totalVeORN: z.number(),
weeklyReward: z.number(),
userAPR: z.number(),
userVeORN: z.number(),
userORNLocked: z.number(),
userLockEndDate: z.number(),
userReward: z.number(),
userWeeklyReward: z.number()
});
const veORNInfoSchema = z.object({
result: veORNResultSchema,
info: infoSchema,
});
export default veORNInfoSchema;

View File

@@ -0,0 +1,33 @@
import { z } from 'zod';
import infoSchema from './info-schema.js';
const poolSchema = z.object({
allVote: z.number(),
name: z.string(),
poolAddress: z.string(),
type: z.string(),
userVote: z.number(),
token0: z.string(), // deprecated
token1: z.string(), // deprecated
name0: z.string(),
name1: z.string(),
poolFee: z.number(),
weight: z.number(),
});
const votingResultSchema = z.object({
absoluteVeTokenInVoting: z.number(),
pools: z.array(poolSchema),
userVeTokenBalance: z.number(),
userVeTokenInVoting: z.number(),
veTokenAddress: z.string(),
votingAddress: z.string(),
weeklyReward: z.number(),
});
const votingInfoSchema = z.object({
result: votingResultSchema,
info: infoSchema,
});
export default votingInfoSchema;

View File

@@ -1,10 +1,10 @@
import { z } from 'zod';
import { SupportedChainId } from '../../../types.js';
import { isAddress } from 'ethers/lib/utils.js';
import { ethers } from 'ethers';
const contractsAddressesSchema = z.record(
z.nativeEnum(SupportedChainId),
z.string().refine(isAddress)
z.string().refine(ethers.isAddress)
);
export default contractsAddressesSchema;

View File

@@ -6,6 +6,7 @@ const ratingSchema = z.object({
weekly_boost_budget_fmt: z.number(),
monthly_boost_budget: z.string(),
monthly_boost_budget_fmt: z.number(),
displayed_boost_budget_fmt: z.number(),
time_left_for_the_reward: z.number(),
time_left_for_the_reward_local: z.string(),
time_left_for_the_reward_utc: z.string(),

View File

@@ -2,3 +2,4 @@ export * as aggregator from './Aggregator/index.js';
export * as blockchainService from './BlockchainService/index.js';
export * as priceFeed from './PriceFeed/index.js';
export * as referralSystem from './ReferralSystem/index.js';
export * as integrator from './Integrator/index.js';