mirror of
https://github.com/orionprotocol/sdk.git
synced 2026-03-17 08:41:38 +03:00
pull main
This commit is contained in:
@@ -31,10 +31,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];
|
||||
@@ -104,7 +109,7 @@ export default class Orion {
|
||||
// api: networkConfig.api,
|
||||
nodeJsonRpc: networkConfig.nodeJsonRpc,
|
||||
services: networkConfig.services,
|
||||
});
|
||||
}, logger);
|
||||
return {
|
||||
...acc,
|
||||
[chainId]: unit,
|
||||
|
||||
27
src/Unit/Exchange/callGenerators/aero.ts
Normal file
27
src/Unit/Exchange/callGenerators/aero.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { SwapExecutor__factory, AeroPool__factory } from "@orionprotocol/contracts/lib/ethers-v6/index.js"
|
||||
import { type BigNumberish, JsonRpcProvider } from "ethers"
|
||||
import { SafeArray } from "../../../utils/safeGetters.js"
|
||||
import { addCallParams } from "./utils.js"
|
||||
import type { SingleSwap } from "../../../types.js"
|
||||
|
||||
export async function generateAeroCalls(
|
||||
path: SafeArray<SingleSwap>,
|
||||
amount: BigNumberish,
|
||||
recipient: string,
|
||||
provider: JsonRpcProvider
|
||||
) {
|
||||
const pools: string[] = [];
|
||||
const direct: boolean[] = [];
|
||||
for (const swap of path) {
|
||||
pools.push(swap.pool);
|
||||
|
||||
const token0 = await AeroPool__factory.connect(swap.pool, provider).token0();
|
||||
direct.push(swap.assetIn.toLowerCase() === token0.toLowerCase());
|
||||
}
|
||||
|
||||
const executorInterface = SwapExecutor__factory.createInterface()
|
||||
let calldata = executorInterface.encodeFunctionData('swapAeroMulti', [pools, direct, amount, recipient]);
|
||||
calldata = addCallParams(calldata)
|
||||
|
||||
return [calldata]
|
||||
}
|
||||
@@ -45,5 +45,5 @@ export async function generateCurveStableSwapCall(
|
||||
}
|
||||
calls.push(calldata)
|
||||
|
||||
return calls
|
||||
return calls
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export function generateTransferCall(
|
||||
target,
|
||||
amount
|
||||
])
|
||||
|
||||
|
||||
return addCallParams(calldata, callParams)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ export function generateApproveCall(
|
||||
target,
|
||||
amount
|
||||
])
|
||||
|
||||
|
||||
return addCallParams(calldata, callParams)
|
||||
}
|
||||
@@ -16,6 +16,6 @@ export function generateFeePaymentCall(
|
||||
token,
|
||||
amount
|
||||
])
|
||||
|
||||
|
||||
return addCallParams(calldata, callParams)
|
||||
}
|
||||
@@ -3,6 +3,15 @@ import { SafeArray } from "../../../utils/safeGetters.js"
|
||||
import { type BytesLike, type BigNumberish, concat, ethers, toBeHex } from "ethers"
|
||||
import { addCallParams } from "./utils.js"
|
||||
import type { SingleSwap } from "../../../types.js"
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
|
||||
const BILLION = 1000000000;
|
||||
const TEN_THOUSANDS = 10000;
|
||||
|
||||
function countScaledFee(fee: string) {
|
||||
// The count is needed for the swapUniV2Scaled function, where the denominator is one billion
|
||||
return new BigNumber(fee).multipliedBy(BILLION).div(TEN_THOUSANDS).toNumber();
|
||||
}
|
||||
|
||||
export async function generateUni2Calls(
|
||||
path: SafeArray<SingleSwap>,
|
||||
@@ -19,17 +28,21 @@ export async function generateUni2Calls(
|
||||
currentSwap.pool,
|
||||
currentSwap.assetIn,
|
||||
currentSwap.assetOut,
|
||||
nextSwap.pool
|
||||
nextSwap.pool,
|
||||
currentSwap.fee
|
||||
)
|
||||
calls.push(call)
|
||||
}
|
||||
}
|
||||
|
||||
const lastSwap = path.last();
|
||||
const calldata = executorInterface.encodeFunctionData('swapUniV2', [
|
||||
const fee = lastSwap.fee ?? 30;
|
||||
const scaledFee = countScaledFee(fee.toString());
|
||||
const calldata = executorInterface.encodeFunctionData('swapUniV2Scaled', [
|
||||
lastSwap.pool,
|
||||
lastSwap.assetIn,
|
||||
lastSwap.assetOut,
|
||||
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat(['0x03', recipient])]),
|
||||
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(scaledFee), recipient])]),
|
||||
])
|
||||
calls.push(addCallParams(calldata))
|
||||
|
||||
@@ -41,14 +54,15 @@ export function generateUni2Call(
|
||||
assetIn: string,
|
||||
assetOut: string,
|
||||
recipient: string,
|
||||
fee: BigNumberish = 3,
|
||||
fee: BigNumberish = 30,
|
||||
) {
|
||||
const executorInterface = SwapExecutor__factory.createInterface()
|
||||
const calldata = executorInterface.encodeFunctionData('swapUniV2', [
|
||||
const scaledFee = countScaledFee(fee.toString());
|
||||
const calldata = executorInterface.encodeFunctionData('swapUniV2Scaled', [
|
||||
pool,
|
||||
assetIn,
|
||||
assetOut,
|
||||
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(fee), recipient])]),
|
||||
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(scaledFee), recipient])]),
|
||||
])
|
||||
return addCallParams(calldata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function generateWrapAndTransferCall(
|
||||
const calldata = executorInterface.encodeFunctionData('wrapAndTransfer', [
|
||||
target,
|
||||
])
|
||||
|
||||
|
||||
return addCallParams(calldata, callParams)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,6 @@ export function generateUnwrapAndTransferCall(
|
||||
target,
|
||||
amount
|
||||
])
|
||||
|
||||
|
||||
return addCallParams(calldata, callParams)
|
||||
}
|
||||
@@ -1,49 +1,51 @@
|
||||
import type { LibValidator } from "@orionprotocol/contracts/lib/ethers-v6/Exchange.js";
|
||||
import { ethers, ZeroAddress } from "ethers";
|
||||
import type { AddressLike, JsonRpcProvider, BigNumberish, BytesLike } from "ethers";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
import { safeGet, SafeArray } from "../../utils/safeGetters.js";
|
||||
import { simpleFetch } from "simple-typed-fetch";
|
||||
import type Unit from "../index.js";
|
||||
import { generateUni2Calls, generateUni2Call } from "./callGenerators/uniswapV2.js";
|
||||
import type { LibValidator } from '@orionprotocol/contracts/lib/ethers-v6/Exchange.js';
|
||||
import { ethers, ZeroAddress } from 'ethers';
|
||||
import type { AddressLike, JsonRpcProvider, BigNumberish, BytesLike } from 'ethers';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { safeGet, SafeArray } from '../../utils/safeGetters.js';
|
||||
import { simpleFetch } from 'simple-typed-fetch';
|
||||
import type Unit from '../index.js';
|
||||
import { generateUni2Calls, generateUni2Call } from './callGenerators/uniswapV2.js';
|
||||
import {
|
||||
generateUni3Calls,
|
||||
generateOrion3Calls,
|
||||
generateUni3Call,
|
||||
generateOrion3Call,
|
||||
} from "./callGenerators/uniswapV3.js";
|
||||
import { exchangeToNativeDecimals, generateCalls, pathCallWithBalance } from "./callGenerators/utils.js";
|
||||
import { generateTransferCall } from "./callGenerators/erc20.js";
|
||||
import { generateCurveStableSwapCall } from "./callGenerators/curve.js";
|
||||
import type { SingleSwap } from "../../types.js";
|
||||
import { addressLikeToString } from "../../utils/addressLikeToString.js";
|
||||
import { generateUnwrapAndTransferCall, generateWrapAndTransferCall } from "./callGenerators/weth.js";
|
||||
import { getExchangeAllowance, getTotalBalance } from "../../utils/getBalance.js";
|
||||
import { generateFeePaymentCall } from "./callGenerators/feePayment.js";
|
||||
} from './callGenerators/uniswapV3.js';
|
||||
import { exchangeToNativeDecimals, generateCalls, pathCallWithBalance } from './callGenerators/utils.js';
|
||||
import { generateTransferCall } from './callGenerators/erc20.js';
|
||||
import { generateCurveStableSwapCall } from './callGenerators/curve.js';
|
||||
import type { SingleSwap } from '../../types.js';
|
||||
import { addressLikeToString } from '../../utils/addressLikeToString.js';
|
||||
import { generateUnwrapAndTransferCall, generateWrapAndTransferCall } from './callGenerators/weth.js';
|
||||
import { getExchangeAllowance, getTotalBalance } from '../../utils/getBalance.js';
|
||||
import { generateFeePaymentCall } from './callGenerators/feePayment.js';
|
||||
import { generateAeroCalls } from './callGenerators/aero.js';
|
||||
|
||||
export type Factory = "UniswapV2" | "UniswapV3" | "Curve" | "OrionV2" | "OrionV3";
|
||||
export type Factory = 'UniswapV2' | 'UniswapV3' | 'Curve' | 'OrionV2' | 'OrionV3' | 'Aero';
|
||||
|
||||
type BaseGenerateSwapCalldataParams = {
|
||||
amount: BigNumberish;
|
||||
minReturnAmount: BigNumberish;
|
||||
initiatorAddress: string;
|
||||
receiverAddress: string;
|
||||
path: ArrayLike<SingleSwap>;
|
||||
matcher?: AddressLike,
|
||||
feeToken?: AddressLike,
|
||||
fee?: BigNumberish;
|
||||
amount: BigNumberish
|
||||
minReturnAmount: BigNumberish
|
||||
initiatorAddress: string
|
||||
receiverAddress: string
|
||||
path: ArrayLike<SingleSwap>
|
||||
matcher?: AddressLike
|
||||
feeToken?: AddressLike
|
||||
fee?: BigNumberish
|
||||
}
|
||||
|
||||
export type GenerateSwapCalldataWithUnitParams = BaseGenerateSwapCalldataParams & {
|
||||
unit: Unit;
|
||||
unit: Unit
|
||||
};
|
||||
|
||||
export type GenerateSwapCalldataParams = BaseGenerateSwapCalldataParams & {
|
||||
exchangeContractAddress: AddressLike;
|
||||
wethAddress: AddressLike;
|
||||
curveRegistryAddress: AddressLike;
|
||||
swapExecutorContractAddress: AddressLike;
|
||||
provider: JsonRpcProvider;
|
||||
exchangeContractAddress: AddressLike
|
||||
wethAddress: AddressLike
|
||||
curveRegistryAddress: AddressLike
|
||||
swapExecutorContractAddress: AddressLike
|
||||
provider: JsonRpcProvider
|
||||
logger?: ((message: string) => void) | undefined
|
||||
};
|
||||
|
||||
export async function generateSwapCalldataWithUnit({
|
||||
@@ -57,29 +59,27 @@ export async function generateSwapCalldataWithUnit({
|
||||
fee = 0,
|
||||
unit,
|
||||
}: GenerateSwapCalldataWithUnitParams): Promise<{
|
||||
calldata: string;
|
||||
swapDescription: LibValidator.SwapDescriptionStruct;
|
||||
value: bigint;
|
||||
calldata: string
|
||||
swapDescription: LibValidator.SwapDescriptionStruct
|
||||
value: bigint
|
||||
}> {
|
||||
if (arrayLikePath == undefined || arrayLikePath.length == 0) {
|
||||
throw new Error("Empty path");
|
||||
throw new Error('Empty path');
|
||||
}
|
||||
const wethAddress = safeGet(unit.contracts, "WETH");
|
||||
const curveRegistryAddress = safeGet(unit.contracts, "curveRegistry");
|
||||
const { assetToAddress, swapExecutorContractAddress, exchangeContractAddress } = await simpleFetch(
|
||||
const wethAddress = safeGet(unit.contracts, 'WETH');
|
||||
const curveRegistryAddress = safeGet(unit.contracts, 'curveRegistry');
|
||||
const { swapExecutorContractAddress, exchangeContractAddress } = await simpleFetch(
|
||||
unit.blockchainService.getInfo
|
||||
)();
|
||||
|
||||
const arrayLikePathCopy = cloneDeep(arrayLikePath);
|
||||
let path = SafeArray.from(arrayLikePathCopy);
|
||||
|
||||
path = SafeArray.from(arrayLikePathCopy).map((swapInfo) => {
|
||||
swapInfo.assetIn = assetToAddress[swapInfo.assetIn] ?? swapInfo.assetIn
|
||||
swapInfo.assetOut = assetToAddress[swapInfo.assetOut] ?? swapInfo.assetOut
|
||||
swapInfo.assetIn = swapInfo.assetIn.toLowerCase()
|
||||
swapInfo.assetOut = swapInfo.assetOut.toLowerCase()
|
||||
return swapInfo;
|
||||
});
|
||||
path = SafeArray.from(arrayLikePathCopy).map((swapInfo) => ({
|
||||
...swapInfo,
|
||||
assetIn: swapInfo.assetAddressIn.toLowerCase(),
|
||||
assetOut: swapInfo.assetAddressOut.toLowerCase(),
|
||||
}));
|
||||
|
||||
return await generateSwapCalldata({
|
||||
amount,
|
||||
@@ -95,6 +95,7 @@ export async function generateSwapCalldataWithUnit({
|
||||
curveRegistryAddress,
|
||||
swapExecutorContractAddress,
|
||||
provider: unit.provider,
|
||||
logger: unit.logger,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,21 +113,30 @@ export async function generateSwapCalldata({
|
||||
curveRegistryAddress: curveRegistryAddressLike,
|
||||
swapExecutorContractAddress: swapExecutorContractAddressLike,
|
||||
provider,
|
||||
logger,
|
||||
}: GenerateSwapCalldataParams): Promise<{
|
||||
calldata: string;
|
||||
swapDescription: LibValidator.SwapDescriptionStruct;
|
||||
value: bigint;
|
||||
calldata: string
|
||||
swapDescription: LibValidator.SwapDescriptionStruct
|
||||
value: bigint
|
||||
}> {
|
||||
const wethAddress = await addressLikeToString(wethAddressLike);
|
||||
logger?.(`wethAddress: ${wethAddress}`);
|
||||
const curveRegistryAddress = await addressLikeToString(curveRegistryAddressLike);
|
||||
logger?.(`curveRegistryAddress: ${curveRegistryAddress}`);
|
||||
const swapExecutorContractAddress = await addressLikeToString(swapExecutorContractAddressLike);
|
||||
logger?.(`swapExecutorContractAddress, ${swapExecutorContractAddress}`);
|
||||
const feeToken = await addressLikeToString(feeTokenAddressLike);
|
||||
logger?.(`feeToken, ${feeToken}`);
|
||||
const matcher = await addressLikeToString(matcherAddressLike);
|
||||
logger?.(`matcher: ${matcher}`);
|
||||
logger?.(`arrayLikePath: ${arrayLikePath}`);
|
||||
let path = SafeArray.from(arrayLikePath).map((swapInfo) => {
|
||||
logger?.(`swapInfo: ${swapInfo}`);
|
||||
swapInfo.assetIn = swapInfo.assetIn.toLowerCase()
|
||||
swapInfo.assetOut = swapInfo.assetOut.toLowerCase()
|
||||
return swapInfo;
|
||||
});
|
||||
logger?.(`path: ${path}`);
|
||||
|
||||
const { assetIn: srcToken } = path.first();
|
||||
const { assetOut: dstToken } = path.last();
|
||||
@@ -140,14 +150,18 @@ export async function generateSwapCalldata({
|
||||
minReturnAmount,
|
||||
flags: 0,
|
||||
};
|
||||
logger?.(`swapDescription: ${swapDescription}`);
|
||||
const amountNativeDecimals = await exchangeToNativeDecimals(srcToken, amount, provider);
|
||||
logger?.(`amountNativeDecimals: ${amountNativeDecimals}`);
|
||||
const feeNativeDecimals = await exchangeToNativeDecimals(feeToken, fee, provider)
|
||||
logger?.(`feeNativeDecimals: ${feeNativeDecimals}`);
|
||||
|
||||
path = SafeArray.from(arrayLikePath).map((singleSwap) => {
|
||||
if (singleSwap.assetIn == ethers.ZeroAddress) singleSwap.assetIn = wethAddress;
|
||||
if (singleSwap.assetOut == ethers.ZeroAddress) singleSwap.assetOut = wethAddress;
|
||||
return singleSwap;
|
||||
});
|
||||
logger?.(`path2: ${path}`);
|
||||
|
||||
let calls: BytesLike[];
|
||||
({ swapDescription, calls } = await processSwaps(
|
||||
@@ -162,19 +176,26 @@ export async function generateSwapCalldata({
|
||||
curveRegistryAddress,
|
||||
provider
|
||||
));
|
||||
logger?.(`swapDescription: ${swapDescription}`);
|
||||
logger?.(`calls: ${calls}`);
|
||||
const calldata = generateCalls(calls);
|
||||
logger?.(`calldata: ${calldata}`);
|
||||
|
||||
const { useExchangeBalance, additionalTransferAmount } = await shouldUseExchangeBalance(
|
||||
srcToken,
|
||||
initiatorAddress,
|
||||
exchangeContractAddress,
|
||||
amountNativeDecimals,
|
||||
provider
|
||||
provider,
|
||||
logger
|
||||
);
|
||||
logger?.(`useExchangeBalance: ${useExchangeBalance}`);
|
||||
logger?.(`additionalTransferAmount: ${additionalTransferAmount}`);
|
||||
if (useExchangeBalance) {
|
||||
swapDescription.flags = 1n << 255n;
|
||||
}
|
||||
const value = srcToken == ZeroAddress ? additionalTransferAmount : 0n;
|
||||
logger?.(`value: ${value}`);
|
||||
return { swapDescription, calldata, value };
|
||||
}
|
||||
|
||||
@@ -214,7 +235,7 @@ async function processSwaps(
|
||||
));
|
||||
}
|
||||
|
||||
({swapDescription, calls} = await payFeeToMatcher(matcher, feeToken, fee, calls, swapDescription));
|
||||
({ swapDescription, calls } = await payFeeToMatcher(matcher, feeToken, fee, calls, swapDescription));
|
||||
|
||||
({ swapDescription, calls } = wrapOrUnwrapIfNeeded(
|
||||
amount,
|
||||
@@ -238,27 +259,31 @@ async function processSingleFactorySwaps(
|
||||
) {
|
||||
let calls: BytesLike[] = [];
|
||||
switch (factory) {
|
||||
case "OrionV2": {
|
||||
case 'OrionV2': {
|
||||
swapDescription.srcReceiver = path.first().pool;
|
||||
calls = await generateUni2Calls(path, swapExecutorContractAddress);
|
||||
break;
|
||||
}
|
||||
case "UniswapV2": {
|
||||
case 'UniswapV2': {
|
||||
swapDescription.srcReceiver = path.first().pool;
|
||||
calls = await generateUni2Calls(path, swapExecutorContractAddress);
|
||||
break;
|
||||
}
|
||||
case "UniswapV3": {
|
||||
case 'UniswapV3': {
|
||||
calls = await generateUni3Calls(path, amount, swapExecutorContractAddress, provider);
|
||||
break;
|
||||
}
|
||||
case "OrionV3": {
|
||||
case 'OrionV3': {
|
||||
calls = await generateOrion3Calls(path, amount, swapExecutorContractAddress, provider);
|
||||
break;
|
||||
}
|
||||
case "Curve": {
|
||||
case "Aero": {
|
||||
calls = await generateAeroCalls(path, amount, swapExecutorContractAddress, provider);
|
||||
break;
|
||||
}
|
||||
case 'Curve': {
|
||||
if (path.length > 1) {
|
||||
throw new Error("Supporting only single stable swap on curve");
|
||||
throw new Error('Supporting only single stable swap on curve');
|
||||
}
|
||||
calls = await generateCurveStableSwapCall(
|
||||
amount,
|
||||
@@ -285,37 +310,37 @@ async function processMultiFactorySwaps(
|
||||
curveRegistryAddress: string,
|
||||
provider: JsonRpcProvider
|
||||
) {
|
||||
let calls: BytesLike[] = [];
|
||||
const calls: BytesLike[] = [];
|
||||
for (const swap of path) {
|
||||
switch (swap.factory) {
|
||||
case "OrionV2": {
|
||||
case 'OrionV2': {
|
||||
let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0);
|
||||
transferCall = pathCallWithBalance(transferCall, swap.assetIn);
|
||||
const uni2Call = generateUni2Call(swap.pool, swap.assetIn, swap.assetOut, swapExecutorContractAddress);
|
||||
const uni2Call = generateUni2Call(swap.pool, swap.assetIn, swap.assetOut, swapExecutorContractAddress, swap.fee);
|
||||
calls.push(transferCall, uni2Call);
|
||||
break;
|
||||
}
|
||||
case "UniswapV2": {
|
||||
case 'UniswapV2': {
|
||||
let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0);
|
||||
transferCall = pathCallWithBalance(transferCall, swap.assetIn);
|
||||
const uni2Call = generateUni2Call(swap.pool, swap.assetIn, swap.assetOut, swapExecutorContractAddress);
|
||||
const uni2Call = generateUni2Call(swap.pool, swap.assetIn, swap.assetOut, swapExecutorContractAddress, swap.fee);
|
||||
calls.push(transferCall, uni2Call);
|
||||
break;
|
||||
}
|
||||
case "UniswapV3": {
|
||||
case 'UniswapV3': {
|
||||
let uni3Call = await generateUni3Call(swap, 0, swapExecutorContractAddress, provider);
|
||||
uni3Call = pathCallWithBalance(uni3Call, swap.assetIn);
|
||||
calls.push(uni3Call);
|
||||
break;
|
||||
}
|
||||
case "OrionV3": {
|
||||
case 'OrionV3': {
|
||||
let orion3Call = await generateOrion3Call(swap, 0, swapExecutorContractAddress, provider);
|
||||
orion3Call = pathCallWithBalance(orion3Call, swap.assetIn);
|
||||
calls.push(orion3Call);
|
||||
break;
|
||||
}
|
||||
case "Curve": {
|
||||
let curveCalls = await generateCurveStableSwapCall(
|
||||
case 'Curve': {
|
||||
const curveCalls = await generateCurveStableSwapCall(
|
||||
amount,
|
||||
swapExecutorContractAddress,
|
||||
swap,
|
||||
@@ -346,7 +371,7 @@ async function payFeeToMatcher(
|
||||
const feePaymentCall = generateFeePaymentCall(matcher, feeToken, feeAmount)
|
||||
calls.push(feePaymentCall)
|
||||
}
|
||||
return {swapDescription, calls}
|
||||
return { swapDescription, calls }
|
||||
}
|
||||
|
||||
function wrapOrUnwrapIfNeeded(
|
||||
@@ -356,7 +381,7 @@ function wrapOrUnwrapIfNeeded(
|
||||
swapExecutorContractAddress: string,
|
||||
wethAddress: string
|
||||
) {
|
||||
const {dstReceiver, srcReceiver, srcToken, dstToken} = swapDescription;
|
||||
const { dstReceiver, srcReceiver, srcToken, dstToken } = swapDescription;
|
||||
if (srcToken === ZeroAddress) {
|
||||
const wrapCall = generateWrapAndTransferCall(srcReceiver, { value: amount });
|
||||
swapDescription.srcReceiver = swapExecutorContractAddress;
|
||||
@@ -379,7 +404,8 @@ async function shouldUseExchangeBalance(
|
||||
initiatorAddress: AddressLike,
|
||||
exchangeContractAddress: AddressLike,
|
||||
amount: bigint,
|
||||
provider: JsonRpcProvider
|
||||
provider: JsonRpcProvider,
|
||||
logger?: ((message: string) => void) | undefined
|
||||
) {
|
||||
const { walletBalance, exchangeBalance } = await getTotalBalance(
|
||||
srcToken,
|
||||
@@ -387,17 +413,19 @@ async function shouldUseExchangeBalance(
|
||||
exchangeContractAddress,
|
||||
provider
|
||||
);
|
||||
|
||||
const exchangeAllowance = await getExchangeAllowance(srcToken, initiatorAddress, exchangeContractAddress, provider);
|
||||
logger?.('test_123');
|
||||
|
||||
if (walletBalance + exchangeBalance < amount) {
|
||||
throw new Error(
|
||||
`Not enough balance to make swap, totalBalance - ${walletBalance + exchangeBalance} swapAmount - ${amount}`
|
||||
`Not enough balance to make swap, walletBalance: ${walletBalance} exchangeBalance: ${exchangeBalance} totalBalance - ${walletBalance + exchangeBalance} swapAmount - ${amount}`
|
||||
);
|
||||
}
|
||||
let useExchangeBalance = true;
|
||||
let additionalTransferAmount = 0n;
|
||||
|
||||
if (exchangeBalance == 0n) {
|
||||
if (walletBalance >= amount || exchangeBalance == 0n) {
|
||||
useExchangeBalance = false;
|
||||
additionalTransferAmount = amount;
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { BlockchainService } from '../../services/BlockchainService/index.j
|
||||
import { calculateFeeInFeeAsset, denormalizeNumber, getNativeCryptocurrencyName } from '../../utils/index.js';
|
||||
|
||||
export type GetSwapInfoParams = {
|
||||
type: 'exactSpend' | 'exactReceive'
|
||||
assetIn: string
|
||||
assetOut: string
|
||||
amount: BigNumber.Value
|
||||
@@ -18,12 +17,12 @@ export type GetSwapInfoParams = {
|
||||
options?: {
|
||||
instantSettlement?: boolean
|
||||
poolOnly?: boolean
|
||||
},
|
||||
walletAddress?: string,
|
||||
}
|
||||
walletAddress?: string
|
||||
isTradeBuy?: boolean
|
||||
}
|
||||
|
||||
export default async function getSwapInfo({
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
amount,
|
||||
@@ -32,6 +31,7 @@ export default async function getSwapInfo({
|
||||
aggregator,
|
||||
options,
|
||||
walletAddress,
|
||||
isTradeBuy = false,
|
||||
}: GetSwapInfoParams) {
|
||||
if (amount === '') throw new Error('Amount can not be empty');
|
||||
if (assetIn === '') throw new Error('AssetIn can not be empty');
|
||||
@@ -61,7 +61,6 @@ export default async function getSwapInfo({
|
||||
}
|
||||
|
||||
const swapInfo = await simpleFetch(aggregator.getSwapInfo)(
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
amountBN.toString(),
|
||||
@@ -69,6 +68,7 @@ export default async function getSwapInfo({
|
||||
options?.poolOnly !== undefined && options.poolOnly
|
||||
? 'pools'
|
||||
: undefined,
|
||||
isTradeBuy,
|
||||
);
|
||||
|
||||
const { exchanges: swapExchanges } = swapInfo;
|
||||
@@ -76,16 +76,6 @@ export default async function getSwapInfo({
|
||||
const poolExchangesList = factories !== undefined ? Object.keys(factories) : [];
|
||||
const [firstSwapExchange] = swapExchanges;
|
||||
|
||||
// if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
// throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`);
|
||||
// }
|
||||
|
||||
// if (swapInfo.type === 'exactSpend' && amountBN.lt(swapInfo.minAmountIn)) {
|
||||
// throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`);
|
||||
// }
|
||||
|
||||
// if (swapInfo.orderInfo === null) throw new Error(swapInfo.executionInfo);
|
||||
|
||||
let route: 'pool' | 'aggregator';
|
||||
if (options?.poolOnly !== undefined && options.poolOnly) {
|
||||
route = 'pool';
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class Exchange {
|
||||
public generateSwapCalldata(params: PureGenerateSwapCalldataParams) {
|
||||
return generateSwapCalldataWithUnit({
|
||||
...params,
|
||||
unit: this.unit
|
||||
unit: this.unit,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import type { SingleSwap } from '../../types.js';
|
||||
import { must, safeGet } from '../../utils/safeGetters.js';
|
||||
|
||||
export type SwapLimitParams = {
|
||||
type: 'exactSpend' | 'exactReceive'
|
||||
assetIn: string
|
||||
assetOut: string
|
||||
price: BigNumber.Value
|
||||
@@ -35,6 +34,7 @@ export type SwapLimitParams = {
|
||||
route?: 'aggregator' | 'pool'
|
||||
}
|
||||
}
|
||||
isTradeBuy?: boolean
|
||||
}
|
||||
|
||||
type AggregatorOrder = {
|
||||
@@ -58,7 +58,6 @@ const isValidSingleSwap = (singleSwap: Omit<SingleSwap, 'factory'> & { factory:
|
||||
}
|
||||
|
||||
export default async function swapLimit({
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
price,
|
||||
@@ -67,6 +66,7 @@ export default async function swapLimit({
|
||||
signer,
|
||||
unit,
|
||||
options,
|
||||
isTradeBuy = false,
|
||||
}: SwapLimitParams): Promise<Swap> {
|
||||
if (options?.developer) options.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!');
|
||||
if (amount === '') throw new Error('Amount can not be empty');
|
||||
@@ -138,7 +138,6 @@ export default async function swapLimit({
|
||||
);
|
||||
|
||||
const swapInfo = await simpleFetch(aggregator.getSwapInfo)(
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
amountBN.toString(),
|
||||
@@ -146,6 +145,7 @@ export default async function swapLimit({
|
||||
options?.poolOnly !== undefined && options.poolOnly
|
||||
? 'pools'
|
||||
: undefined,
|
||||
isTradeBuy,
|
||||
);
|
||||
|
||||
const { exchanges: swapExchanges, exchangeContractPath } = swapInfo;
|
||||
@@ -154,11 +154,11 @@ export default async function swapLimit({
|
||||
|
||||
if (swapExchanges.length > 0) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`);
|
||||
|
||||
if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
if (swapInfo?.isTradeBuy && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`);
|
||||
}
|
||||
|
||||
if (swapInfo.type === 'exactSpend' && amountBN.lt(swapInfo.minAmountIn)) {
|
||||
if (!(swapInfo?.isTradeBuy) && amountBN.lt(swapInfo.minAmountIn)) {
|
||||
throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`);
|
||||
}
|
||||
|
||||
@@ -200,9 +200,9 @@ export default async function swapLimit({
|
||||
|
||||
options?.logger?.(`Safe price is ${swapInfo.orderInfo.safePrice} ${quoteAssetName}`);
|
||||
// BTEMP — better than or equal market price
|
||||
const priceIsBTEMP = type === 'exactSpend'
|
||||
? priceBN.lte(swapInfo.orderInfo.safePrice)
|
||||
: priceBN.gte(swapInfo.orderInfo.safePrice);
|
||||
const priceIsBTEMP = isTradeBuy
|
||||
? priceBN.gte(swapInfo.orderInfo.safePrice)
|
||||
: priceBN.lte(swapInfo.orderInfo.safePrice);
|
||||
|
||||
options?.logger?.(`Your price ${priceBN.toString()} is ${priceIsBTEMP ? 'better than or equal' : 'worse than'} market price ${swapInfo.orderInfo.safePrice}`);
|
||||
|
||||
@@ -246,7 +246,7 @@ export default async function swapLimit({
|
||||
if (factoryAddress !== undefined) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`);
|
||||
}
|
||||
|
||||
const amountSpend = swapInfo.type === 'exactSpend'
|
||||
const amountSpend = !(swapInfo?.isTradeBuy)
|
||||
? swapInfo.amountIn
|
||||
: new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice)
|
||||
|
||||
@@ -261,7 +261,7 @@ export default async function swapLimit({
|
||||
sources: getAvailableSources('amount', assetInAddress, 'pool'),
|
||||
});
|
||||
|
||||
const amountReceive = swapInfo.type === 'exactReceive'
|
||||
const amountReceive = swapInfo?.isTradeBuy
|
||||
? swapInfo.amountOut
|
||||
: new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice)
|
||||
const amountSpendBlockchainParam = normalizeNumber(
|
||||
|
||||
@@ -37,13 +37,11 @@ type PoolSwap = {
|
||||
|
||||
export type Swap = AggregatorOrder | PoolSwap;
|
||||
|
||||
|
||||
const isValidSingleSwap = (singleSwap: Omit<SingleSwap, 'factory'> & { factory: string }): singleSwap is SingleSwap => {
|
||||
return isValidFactory(singleSwap.factory);
|
||||
}
|
||||
|
||||
export default async function swapMarket({
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
amount,
|
||||
@@ -52,6 +50,7 @@ export default async function swapMarket({
|
||||
signer,
|
||||
unit,
|
||||
options,
|
||||
isTradeBuy = false,
|
||||
}: SwapMarketParams): Promise<Swap> {
|
||||
if (options?.developer) options.logger?.('YOU SPECIFIED A DEVELOPER OPTIONS. BE CAREFUL!');
|
||||
|
||||
@@ -125,7 +124,6 @@ export default async function swapMarket({
|
||||
);
|
||||
|
||||
const swapInfo = await simpleFetch(aggregator.getSwapInfo)(
|
||||
type,
|
||||
assetIn,
|
||||
assetOut,
|
||||
amountBN.toString(),
|
||||
@@ -133,6 +131,7 @@ export default async function swapMarket({
|
||||
options?.poolOnly !== undefined && options.poolOnly
|
||||
? 'pools'
|
||||
: undefined,
|
||||
isTradeBuy,
|
||||
);
|
||||
|
||||
const { exchanges: swapExchanges, exchangeContractPath } = swapInfo;
|
||||
@@ -141,11 +140,11 @@ export default async function swapMarket({
|
||||
|
||||
if (swapExchanges.length > 0) options?.logger?.(`Swap exchanges: ${swapExchanges.join(', ')}`);
|
||||
|
||||
if (swapInfo.type === 'exactReceive' && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
if (swapInfo?.isTradeBuy && amountBN.lt(swapInfo.minAmountOut)) {
|
||||
throw new Error(`Amount is too low. Min amountOut is ${swapInfo.minAmountOut} ${assetOut}`);
|
||||
}
|
||||
|
||||
if (swapInfo.type === 'exactSpend' && amountBN.lt(swapInfo.minAmountIn)) {
|
||||
if (!(swapInfo?.isTradeBuy) && amountBN.lt(swapInfo.minAmountIn)) {
|
||||
throw new Error(`Amount is too low. Min amountIn is ${swapInfo.minAmountIn} ${assetIn}`);
|
||||
}
|
||||
|
||||
@@ -203,7 +202,7 @@ export default async function swapMarket({
|
||||
.multipliedBy(new BigNumber(1).plus(percent))
|
||||
.toString();
|
||||
|
||||
const amountSpend = swapInfo.type === 'exactSpend' ? swapInfo.amountIn : amountInWithSlippage;
|
||||
const amountSpend = swapInfo?.isTradeBuy ? amountInWithSlippage : swapInfo.amountIn;
|
||||
|
||||
balanceGuard.registerRequirement({
|
||||
reason: 'Amount spend',
|
||||
@@ -216,7 +215,7 @@ export default async function swapMarket({
|
||||
sources: getAvailableSources('amount', assetInAddress, 'pool'),
|
||||
});
|
||||
|
||||
const amountReceive = swapInfo.type === 'exactReceive' ? swapInfo.amountOut : amountOutWithSlippage;
|
||||
const amountReceive = swapInfo?.isTradeBuy ? amountOutWithSlippage : swapInfo.amountOut;
|
||||
const amountSpendBlockchainParam = normalizeNumber(
|
||||
amountSpend,
|
||||
INTERNAL_PROTOCOL_PRECISION,
|
||||
|
||||
@@ -79,6 +79,6 @@ export default class Pmm {
|
||||
const contract = new ethers.Contract(this.contractAddress, orionRFQContractABI, signer);
|
||||
|
||||
// @ts-ignore
|
||||
return contract.fillOrderRFQ(order.quotation, order.signature, BigInt(0));
|
||||
return contract.fillOrderRFQ(order.order, order.signature, BigInt(0));
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export const pmmOrderQuotationSchema = z.object({
|
||||
});
|
||||
|
||||
export const pmmOrderSchema = z.object({
|
||||
quotation: pmmOrderQuotationSchema.default({}),
|
||||
order: pmmOrderQuotationSchema.default({}),
|
||||
signature: z.string().default(''),
|
||||
success: z.boolean().default(false),
|
||||
error: z.string().default(''),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('Orion', () => {
|
||||
test('Init Orion testing', () => {
|
||||
const orion = new Orion('testing');
|
||||
expect(orion.referralSystem).toBeInstanceOf(ReferralSystem);
|
||||
expect(orion.unitsArray.length).toBe(4); // eth, bsc, polygon, fantom
|
||||
expect(orion.unitsArray.length).toBe(2); // eth, bsc
|
||||
|
||||
const unitBSC = orion.units[SupportedChainId.BSC_TESTNET];
|
||||
expect(unitBSC?.chainId).toBe(SupportedChainId.BSC_TESTNET);
|
||||
@@ -89,23 +89,11 @@ describe('Orion', () => {
|
||||
expect(orion.getSiblingsOf(SupportedChainId.BSC_TESTNET)).toHaveLength(3);
|
||||
expect(unitBSC?.networkCode).toBe('bsc');
|
||||
|
||||
const unitRopsten = orion.units[SupportedChainId.ROPSTEN]
|
||||
expect(unitRopsten?.chainId).toBe(SupportedChainId.ROPSTEN);
|
||||
const unitSepolia = orion.units[SupportedChainId.SEPOLIA]
|
||||
expect(unitSepolia?.chainId).toBe(SupportedChainId.SEPOLIA);
|
||||
// expect(unitRopsten?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.ROPSTEN)).toHaveLength(3);
|
||||
expect(unitRopsten?.networkCode).toBe('eth');
|
||||
|
||||
const unitPolygon = orion.units[SupportedChainId.POLYGON_TESTNET];
|
||||
expect(unitPolygon?.chainId).toBe(SupportedChainId.POLYGON_TESTNET);
|
||||
// expect(unitPolygon?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.POLYGON_TESTNET)).toHaveLength(3);
|
||||
expect(unitPolygon?.networkCode).toBe('polygon');
|
||||
|
||||
const unitFantom = orion.units[SupportedChainId.FANTOM_TESTNET];
|
||||
expect(unitFantom?.chainId).toBe(SupportedChainId.FANTOM_TESTNET);
|
||||
// expect(unitFantom?.env).toBe('testing');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.FANTOM_TESTNET)).toHaveLength(3);
|
||||
expect(unitFantom?.networkCode).toBe('ftm');
|
||||
expect(orion.getSiblingsOf(SupportedChainId.SEPOLIA)).toHaveLength(3);
|
||||
expect(unitSepolia?.networkCode).toBe('eth');
|
||||
});
|
||||
|
||||
test('Init Orion production', () => {
|
||||
|
||||
@@ -51,42 +51,42 @@
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"chainId": "3",
|
||||
"explorer": "https://ropsten.etherscan.io/",
|
||||
"label": "Ropsten",
|
||||
"shortName": "ETH-Ropsten",
|
||||
"11155111": {
|
||||
"chainId": "11155111",
|
||||
"explorer": "https://sepolia.etherscan.io/",
|
||||
"label": "Sepolia",
|
||||
"shortName": "ETH-Sepolia",
|
||||
"code": "eth",
|
||||
"rpc": "https://testing.orion.xyz/eth-ropsten/rpc",
|
||||
"rpc": "https://gateway.tenderly.co/public/sepolia",
|
||||
"baseCurrencyName": "ETH",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"chainId": "5",
|
||||
"explorer": "https://goerli.etherscan.io/",
|
||||
"label": "Goerli",
|
||||
"shortName": "ETH-Goerli",
|
||||
"123420000034": {
|
||||
"chainId": "123420000034",
|
||||
"explorer": "https://blockscout-123420000034.raas-testnet.gelato.digital/",
|
||||
"label": "Event Horizon Testnet",
|
||||
"shortName": "EH-Testnet",
|
||||
"code": "eth",
|
||||
"rpc": "https://testing.orion.xyz/eth-goerli/rpc",
|
||||
"rpc": "https://rpc-123420000034.raas-testnet.gelato.digital/",
|
||||
"baseCurrencyName": "ETH",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"WETH": "0x4200000000000000000000000000000000000006",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"421613": {
|
||||
"chainId": "421613",
|
||||
"explorer": "https://goerli.arbiscan.io/",
|
||||
"label": "Arbitrum Goerli",
|
||||
"shortName": "Arbitrum Goerli",
|
||||
"code": "arb",
|
||||
"rpc": "https://goerli-rollup.arbitrum.io/rpc",
|
||||
"baseCurrencyName": "ETH",
|
||||
"1952959480": {
|
||||
"chainId": "1952959480",
|
||||
"explorer": "https://testnet-explorer.lumia.org/",
|
||||
"label": "Lumia Testnet",
|
||||
"shortName": "Lumia Testnet",
|
||||
"code": "lumia",
|
||||
"rpc": "https://testnet-rpc.lumia.org",
|
||||
"baseCurrencyName": "LUMIA",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"WETH": "0x1a1aF9C78704D3a0Ab9e031C92E7bd808711A582",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
@@ -103,19 +103,6 @@
|
||||
"curveRegistry": "0x445FE580eF8d70FF569aB36e80c647af338db351"
|
||||
}
|
||||
},
|
||||
"4002": {
|
||||
"chainId": "4002",
|
||||
"explorer": "https://testnet.ftmscan.com/",
|
||||
"label": "Fantom Testnet",
|
||||
"shortName": "FTM-Testnet",
|
||||
"code": "ftm",
|
||||
"rpc": "https://rpc.testnet.fantom.network/",
|
||||
"baseCurrencyName": "FTM",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"250": {
|
||||
"chainId": "250",
|
||||
"explorer": "https://ftmscan.com/",
|
||||
@@ -142,19 +129,6 @@
|
||||
"curveRegistry": "0x094d12e5b541784701FD8d65F11fc0598FBC6332"
|
||||
}
|
||||
},
|
||||
"80001": {
|
||||
"chainId": "80001",
|
||||
"label": "Polygon Mumbai",
|
||||
"shortName": "Polygon Mumbai",
|
||||
"code": "polygon",
|
||||
"baseCurrencyName": "MATIC",
|
||||
"rpc": "https://rpc.ankr.com/polygon_mumbai",
|
||||
"explorer": "https://mumbai.polygonscan.com/",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"66": {
|
||||
"chainId": "66",
|
||||
"explorer": "https://www.oklink.com/okc/",
|
||||
@@ -168,32 +142,6 @@
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"65": {
|
||||
"chainId": "65",
|
||||
"explorer": "https://www.oklink.com/okc-test/",
|
||||
"label": "OKC Testnet",
|
||||
"shortName": "OKC-Testnet",
|
||||
"code": "okc",
|
||||
"rpc": "https://exchaintestrpc.okex.org/",
|
||||
"baseCurrencyName": "OKT",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"56303": {
|
||||
"chainId": "56303",
|
||||
"label": "DRIP Chain",
|
||||
"shortName": "DRIP Chain",
|
||||
"code": "drip",
|
||||
"baseCurrencyName": "DRIP",
|
||||
"rpc": "https://testnet.1d.rip/",
|
||||
"explorer": "https://explorer-testnet.1d.rip/",
|
||||
"contracts": {
|
||||
"WETH": "",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
},
|
||||
"2525": {
|
||||
"chainId": "2525",
|
||||
"label": "inEVM",
|
||||
@@ -219,5 +167,31 @@
|
||||
"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": ""
|
||||
}
|
||||
},
|
||||
"8453": {
|
||||
"chainId": "8453",
|
||||
"label": "Base",
|
||||
"shortName": "BASE",
|
||||
"code": "base",
|
||||
"baseCurrencyName": "ETH",
|
||||
"rpc": "https://mainnet.base.org/",
|
||||
"explorer": "https://basescan.org/",
|
||||
"contracts": {
|
||||
"WETH": "0x4200000000000000000000000000000000000006",
|
||||
"curveRegistry": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,42 @@
|
||||
"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/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"8453": {
|
||||
"api": "https://trade.orion.xyz/base-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -191,8 +227,8 @@
|
||||
},
|
||||
"liquidityMigratorAddress": "0x01b10dds12478C88A5E18e2707E729906bC25CfF6"
|
||||
},
|
||||
"5": {
|
||||
"api": "https://testing.orion.xyz/eth-goerli",
|
||||
"11155111": {
|
||||
"api": "https://testing.orion.xyz/eth-sepolia",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -209,8 +245,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"421613": {
|
||||
"api": "https://testing.orion.xyz/arbitrum-goerli",
|
||||
"123420000034": {
|
||||
"api": "https://testing.orion.xyz/event-horizon-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -227,44 +263,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"4002": {
|
||||
"api": "https://testing.orion.xyz/ftm-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"80001": {
|
||||
"api": "https://testing.orion.xyz/polygon-mumbai",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"56303": {
|
||||
"api": "https://testing.orion.xyz/drip-testnet",
|
||||
"1952959480": {
|
||||
"api": "https://testing.orion.xyz/lumia-testnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -414,7 +414,7 @@
|
||||
}
|
||||
},
|
||||
"2525": {
|
||||
"api": "https://trade.orion.xyz/inevm-mainnet",
|
||||
"api": "https://staging.orion.xyz/inevm-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -432,7 +432,43 @@
|
||||
}
|
||||
},
|
||||
"59144": {
|
||||
"api": "https://trade.orion.xyz/linea-mainnet",
|
||||
"api": "https://staging.orion.xyz/linea-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"43114": {
|
||||
"api": "https://staging.orion.xyz/avalanche-c-chain",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"8453": {
|
||||
"api": "https://staging.orion.xyz/base-mainnet",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
@@ -472,24 +508,6 @@
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"api": "https://dn-dev.orion.xyz/eth-ropsten",
|
||||
"services": {
|
||||
"aggregator": {
|
||||
"http": "/backend",
|
||||
"ws": "/v1"
|
||||
},
|
||||
"blockchain": {
|
||||
"http": ""
|
||||
},
|
||||
"priceFeed": {
|
||||
"all": "/price-feed"
|
||||
},
|
||||
"indexer": {
|
||||
"http": "/orion-indexer/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ export const pureEnvNetworksSchema = z.object({
|
||||
}),
|
||||
indexer: z.object({
|
||||
http: z.string(),
|
||||
}).optional(),
|
||||
}).optional()
|
||||
}),
|
||||
rpc: z.string().optional(),
|
||||
liquidityMigratorAddress: z.string().optional(),
|
||||
|
||||
@@ -2,12 +2,9 @@ import { SupportedChainId } from '../types.js';
|
||||
|
||||
export const developmentChains = [
|
||||
SupportedChainId.BSC_TESTNET,
|
||||
SupportedChainId.ROPSTEN,
|
||||
SupportedChainId.GOERLI,
|
||||
SupportedChainId.ARBITRUM_GOERLI,
|
||||
SupportedChainId.FANTOM_TESTNET,
|
||||
SupportedChainId.POLYGON_TESTNET,
|
||||
SupportedChainId.OKC_TESTNET,
|
||||
SupportedChainId.SEPOLIA,
|
||||
SupportedChainId.EVENT_HORIZON_TESTNET,
|
||||
SupportedChainId.LUMIA_TESTNET,
|
||||
];
|
||||
export const productionChains = [
|
||||
SupportedChainId.MAINNET,
|
||||
@@ -19,4 +16,6 @@ export const productionChains = [
|
||||
SupportedChainId.OPBNB,
|
||||
SupportedChainId.INEVM,
|
||||
SupportedChainId.LINEA,
|
||||
SupportedChainId.AVAX,
|
||||
SupportedChainId.BASE,
|
||||
];
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default ["UniswapV2", "UniswapV3", "Curve", "OrionV2", "OrionV3"] as const
|
||||
export default ["UniswapV2", "UniswapV3", "Curve", "OrionV2", "OrionV3", "Aero"] as const
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default ['ftm', 'bsc', 'eth', 'polygon', 'okc', 'arb', 'drip', 'opbnb', 'inevm', 'linea'] as const;
|
||||
export default ['ftm', 'bsc', 'eth', 'polygon', 'okc', 'arb', 'opbnb', 'inevm', 'linea', 'avax', 'base', 'lumia'] as const;
|
||||
|
||||
@@ -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', 'BASE', 'LUMIA'] as const;
|
||||
|
||||
@@ -19,9 +19,10 @@ import httpToWS from '../../utils/httpToWS.js';
|
||||
import { ethers } from 'ethers';
|
||||
import orderSchema from './schemas/orderSchema.js';
|
||||
import { fetchWithValidation } from 'simple-typed-fetch';
|
||||
import hmacSHA256 from "crypto-js/hmac-sha256";
|
||||
import Hex from "crypto-js/enc-hex";
|
||||
import {pmmOrderSchema} from "../../Unit/Pmm/schemas/order";
|
||||
import { pmmOrderSchema } from '../../Unit/Pmm/schemas/order';
|
||||
// import hmacSHA256 from "crypto-js/hmac-sha256";
|
||||
// import Hex from "crypto-js/enc-hex";
|
||||
// const crypto = require('crypto')
|
||||
|
||||
class Aggregator {
|
||||
private readonly apiUrl: string;
|
||||
@@ -34,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 === '/'
|
||||
@@ -46,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);
|
||||
@@ -55,6 +61,7 @@ class Aggregator {
|
||||
this.getPairsList = this.getPairsList.bind(this);
|
||||
this.getSwapInfo = this.getSwapInfo.bind(this);
|
||||
this.getTradeProfits = this.getTradeProfits.bind(this);
|
||||
this.getStableCoins = this.getStableCoins.bind(this);
|
||||
this.placeAtomicSwap = this.placeAtomicSwap.bind(this);
|
||||
this.placeOrder = this.placeOrder.bind(this);
|
||||
this.cancelOrder = this.cancelOrder.bind(this);
|
||||
@@ -65,6 +72,7 @@ class Aggregator {
|
||||
this.getPoolReserves = this.getPoolReserves.bind(this);
|
||||
this.getVersion = this.getVersion.bind(this);
|
||||
this.getPrices = this.getPrices.bind(this);
|
||||
this.getIsCexLiquidityAvailable = this.getIsCexLiquidityAvailable.bind(this);
|
||||
}
|
||||
|
||||
get basicAuthHeaders() {
|
||||
@@ -255,21 +263,22 @@ class Aggregator {
|
||||
);
|
||||
|
||||
getSwapInfo = (
|
||||
type: 'exactSpend' | 'exactReceive',
|
||||
assetIn: string,
|
||||
assetOut: string,
|
||||
amount: string,
|
||||
instantSettlement?: boolean,
|
||||
exchanges?: string[] | 'cex' | 'pools',
|
||||
isTradeBuy?: boolean,
|
||||
) => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/swap`);
|
||||
url.searchParams.append('assetIn', assetIn);
|
||||
url.searchParams.append('assetOut', assetOut);
|
||||
if (type === 'exactSpend') {
|
||||
if (isTradeBuy !== true) {
|
||||
url.searchParams.append('amountIn', amount);
|
||||
} else {
|
||||
url.searchParams.append('amountOut', amount);
|
||||
}
|
||||
|
||||
if (exchanges !== undefined) {
|
||||
if (Array.isArray(exchanges)) {
|
||||
exchanges.forEach((exchange) => {
|
||||
@@ -334,6 +343,16 @@ class Aggregator {
|
||||
);
|
||||
};
|
||||
|
||||
getStableCoins = () => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/tokens/stable/`);
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
z.array(z.string()),
|
||||
{ headers: this.basicAuthHeaders },
|
||||
errorSchema,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Placing atomic swap. Placement must take place on the target chain.
|
||||
* @param secretHash Secret hash
|
||||
@@ -373,24 +392,38 @@ class Aggregator {
|
||||
return fetchWithValidation(url.toString(), atomicSwapHistorySchema, { headers: this.basicAuthHeaders });
|
||||
};
|
||||
|
||||
getIsCexLiquidityAvailable = (
|
||||
assetIn: string,
|
||||
assetOut: string,
|
||||
) => {
|
||||
const url = new URL(`${this.apiUrl}/api/v1/pairs/cex/liquidity/${assetIn}/${assetOut}`);
|
||||
|
||||
private encode_utf8(s : string) {
|
||||
return unescape(encodeURIComponent(s));
|
||||
return fetchWithValidation(
|
||||
url.toString(),
|
||||
z.boolean(),
|
||||
{ headers: this.basicAuthHeaders },
|
||||
errorSchema,
|
||||
);
|
||||
};
|
||||
|
||||
// private encode_utf8(s: string) {
|
||||
// return unescape(encodeURIComponent(s));
|
||||
// }
|
||||
|
||||
// @ts-expect-error: TODO: please remove this line!
|
||||
private sign(message: string, key: string) {
|
||||
// return crypto.createHmac('sha256', this.encode_utf8(key))
|
||||
// .update(this.encode_utf8(message))
|
||||
// .digest('hex');
|
||||
return '';
|
||||
}
|
||||
|
||||
private sign(message : string, key: string) {
|
||||
return hmacSHA256(
|
||||
this.encode_utf8(message),
|
||||
this.encode_utf8(key)
|
||||
).toString(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 +440,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,25 +480,23 @@ 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);
|
||||
}
|
||||
|
||||
res.quotation = parseResult.data.quotation;
|
||||
res.order = parseResult.data.order;
|
||||
res.signature = parseResult.data.signature;
|
||||
res.error = '';
|
||||
res.success = true;
|
||||
// return result;
|
||||
}
|
||||
catch(err) {
|
||||
} catch (err) {
|
||||
res.error = `${err}`;
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -12,6 +12,8 @@ const exchangeContractStep = z.object({
|
||||
assetIn: z.string(),
|
||||
assetOut: z.string(),
|
||||
factory: z.string(),
|
||||
assetAddressIn: z.string(),
|
||||
assetAddressOut: z.string(),
|
||||
});
|
||||
|
||||
const swapInfoBase = z.object({
|
||||
@@ -49,6 +51,7 @@ const swapInfoBase = z.object({
|
||||
mi: z.number().optional(), // market amount in, USD
|
||||
d: z.string().optional(), // difference in available amount in/out (USD) and market amount out/in (USD) in percentage
|
||||
}).optional(),
|
||||
autoSlippage: z.number().optional(),
|
||||
});
|
||||
|
||||
const swapInfoByAmountIn = swapInfoBase.extend({
|
||||
@@ -58,7 +61,7 @@ const swapInfoByAmountIn = swapInfoBase.extend({
|
||||
marketAmountIn: z.null(),
|
||||
}).transform((val) => ({
|
||||
...val,
|
||||
type: 'exactSpend' as const,
|
||||
isTradeBuy: false as const,
|
||||
}));
|
||||
|
||||
const swapInfoByAmountOut = swapInfoBase.extend({
|
||||
@@ -68,7 +71,7 @@ const swapInfoByAmountOut = swapInfoBase.extend({
|
||||
marketAmountIn: z.number().nullable(),
|
||||
}).transform((val) => ({
|
||||
...val,
|
||||
type: 'exactReceive' as const,
|
||||
isTradeBuy: true as const,
|
||||
}));
|
||||
|
||||
const swapInfoSchema = swapInfoByAmountIn.or(swapInfoByAmountOut);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -506,6 +514,9 @@ class AggregatorWS {
|
||||
assetIn: path.ai,
|
||||
assetOut: path.ao,
|
||||
factory: path.f,
|
||||
assetAddressIn: path.aai,
|
||||
assetAddressOut: path.aao,
|
||||
fee: path.fee,
|
||||
})),
|
||||
poolOptimal: json.po,
|
||||
...(json.oi) && {
|
||||
@@ -533,21 +544,22 @@ class AggregatorWS {
|
||||
marketAmountIn: json.usd.mi,
|
||||
difference: json.usd.d,
|
||||
},
|
||||
autoSlippage: json.sl,
|
||||
};
|
||||
|
||||
switch (json.k) { // kind
|
||||
case 'exactSpend':
|
||||
switch (json.tb) { // isTradeBuy
|
||||
case false:
|
||||
this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[json.S]?.callback({
|
||||
kind: json.k,
|
||||
isTradeBuy: false,
|
||||
marketAmountOut: json.mo,
|
||||
availableAmountIn: json.aa,
|
||||
...baseSwapInfo,
|
||||
});
|
||||
|
||||
break;
|
||||
case 'exactReceive':
|
||||
case true:
|
||||
this.subscriptions[SubscriptionType.SWAP_SUBSCRIBE]?.[json.S]?.callback({
|
||||
kind: json.k,
|
||||
isTradeBuy: true,
|
||||
...baseSwapInfo,
|
||||
marketAmountIn: json.mi,
|
||||
availableAmountOut: json.aao,
|
||||
|
||||
@@ -40,6 +40,9 @@ const swapInfoSchemaBase = baseMessageSchema.extend({
|
||||
ai: z.string().toUpperCase(), // asset in
|
||||
ao: z.string().toUpperCase(), // asset out
|
||||
f: factorySchema, // factory
|
||||
aai: z.string(), // asset address in
|
||||
aao: z.string(), // asset address out
|
||||
fee: z.number().optional(), // fee
|
||||
})),
|
||||
usd: z.object({ // USD info of this swap, nullable
|
||||
aa: z.number().optional(), // available amount in, USD
|
||||
@@ -48,6 +51,7 @@ const swapInfoSchemaBase = baseMessageSchema.extend({
|
||||
mi: z.number().optional(), // market amount in, USD
|
||||
d: z.string().optional(), // difference in available amount in/out (USD) and market amount out/in (USD) in percentage
|
||||
}).optional(),
|
||||
sl: z.number().optional(),
|
||||
});
|
||||
|
||||
const swapInfoSchemaByAmountIn = swapInfoSchemaBase.extend({
|
||||
@@ -55,7 +59,7 @@ const swapInfoSchemaByAmountIn = swapInfoSchemaBase.extend({
|
||||
aa: z.number(), // available amount in
|
||||
}).transform((content) => ({
|
||||
...content,
|
||||
k: 'exactSpend' as const,
|
||||
tb: false as const, // isTradeBuy
|
||||
}));
|
||||
|
||||
const swapInfoSchemaByAmountOut = swapInfoSchemaBase.extend({
|
||||
@@ -63,7 +67,7 @@ const swapInfoSchemaByAmountOut = swapInfoSchemaBase.extend({
|
||||
aao: z.number(), // available amount out
|
||||
}).transform((content) => ({
|
||||
...content,
|
||||
k: 'exactReceive' as const,
|
||||
tb: true as const, // isTradeBuy
|
||||
}));
|
||||
|
||||
const swapInfoSchema = z.union([
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { makePartial } from '../../../utils/index.js';
|
||||
import { makePartial } from '../../../utils';
|
||||
|
||||
const internalFeeAssetSchema = z.object({
|
||||
type: z.enum(['percent', 'plain']),
|
||||
@@ -10,8 +10,10 @@ const internalFeeAssetSchema = z.object({
|
||||
const infoSchema = z.object({
|
||||
chainId: z.number(),
|
||||
chainName: z.string(),
|
||||
exchangeContractAddress: z.string(),
|
||||
swapExecutorContractAddress: z.string(),
|
||||
libValidatorContractAddress: z.string().optional(),
|
||||
exchangeContractAddress: z.string(),
|
||||
spvContractAddress: z.string(),
|
||||
oracleContractAddress: z.string(),
|
||||
matcherAddress: z.string(),
|
||||
orderFeePercent: z.number(),
|
||||
|
||||
13
src/services/Frontage/schemas/tickers-schema.ts
Normal file
13
src/services/Frontage/schemas/tickers-schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
import { SupportedChainId } from '../../../types';
|
||||
|
||||
export const tickerSchema = z.object({
|
||||
pair: z.string(),
|
||||
volume24: z.number(),
|
||||
change24: z.number(),
|
||||
lastPrice: z.number(),
|
||||
pricePrecision: z.number(),
|
||||
networks: z.array(z.nativeEnum(SupportedChainId)),
|
||||
});
|
||||
|
||||
export const tickersSchema = z.array(tickerSchema);
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
environmentResponseSchema,
|
||||
getPointsAtResponseSchema,
|
||||
getPoolResponseSchema,
|
||||
listAmountResponseSchema,
|
||||
listNFTOrderResponseSchema,
|
||||
@@ -51,6 +52,12 @@ type VeORNInfoPayload = BasePayload & {
|
||||
params: [string]
|
||||
};
|
||||
|
||||
type GetPointsAtPayload = BasePayload & {
|
||||
model: 'veORN'
|
||||
method: 'pointsInfo'
|
||||
params: [number, number]
|
||||
};
|
||||
|
||||
type ListAmountPayload = BasePayload & {
|
||||
model: string
|
||||
method: 'listAmount'
|
||||
@@ -68,6 +75,7 @@ type Payload =
|
||||
| GetPoolInfoPayload
|
||||
| ListPoolPayload
|
||||
| VeORNInfoPayload
|
||||
| GetPointsAtPayload
|
||||
| ListAmountPayload
|
||||
| GetAmountByORNPayload;
|
||||
|
||||
@@ -92,6 +100,7 @@ class IndexerService {
|
||||
this.poolV2Info = this.poolV2Info.bind(this);
|
||||
this.listPoolV3 = this.listPoolV3.bind(this);
|
||||
this.veORNInfo = this.veORNInfo.bind(this);
|
||||
this.getPointsAt = this.getPointsAt.bind(this);
|
||||
this.listAmount = this.listAmount.bind(this);
|
||||
this.getAmountByORN = this.getAmountByORN.bind(this);
|
||||
this.getAmountAt = this.getAmountAt.bind(this);
|
||||
@@ -118,6 +127,21 @@ class IndexerService {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} page - current page
|
||||
* @param {number} [pageSize] - amount of items on one page
|
||||
*/
|
||||
readonly getPointsAt = (page = 1, pageSize = 1000) => {
|
||||
return fetchWithValidation(this.apiUrl, getPointsAtResponseSchema, {
|
||||
method: 'POST',
|
||||
body: this.makeRPCPayload({
|
||||
model: 'veORN',
|
||||
method: 'pointsAt',
|
||||
params: [page, pageSize],
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} amount - amount
|
||||
* @param {number} [timestamp = Date.now()] - timestamp, defaults to current time
|
||||
|
||||
15
src/services/Indexer/schemas/get-points-at-schema.ts
Normal file
15
src/services/Indexer/schemas/get-points-at-schema.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
import infoSchema from './info-schema.js';
|
||||
|
||||
const getPointsAtResultSchema = z.object({
|
||||
pointsObject: z.record(z.string(), z.number()),
|
||||
currentPage: z.number(),
|
||||
totalElements: z.number(),
|
||||
});
|
||||
|
||||
const getPointsAtSchema = z.object({
|
||||
result: getPointsAtResultSchema,
|
||||
info: infoSchema,
|
||||
}).nullable();
|
||||
|
||||
export default getPointsAtSchema;
|
||||
@@ -9,3 +9,4 @@ export { default as veORNInfoResponseSchema } from './veORN-info-schema';
|
||||
export { default as listAmountResponseSchema } from './list-amount-schema';
|
||||
export { default as votingInfoResponseSchema } from './voting-info-schema';
|
||||
export { default as testIncrementorSchema } from './test-incrementor-schema';
|
||||
export { default as getPointsAtResponseSchema } from './get-points-at-schema';
|
||||
|
||||
@@ -12,12 +12,15 @@ const veORNResultSchema = z.object({
|
||||
weeklyReward: z.number(),
|
||||
userAPR: z.number(),
|
||||
userVeORN: z.number(),
|
||||
userVeORNBalance: z.number(),
|
||||
userORNLocked: z.number(),
|
||||
userLockEndDate: z.number(),
|
||||
userReward: z.number(),
|
||||
userWeeklyReward: z.number(),
|
||||
userMinLockPeriod: z.number(),
|
||||
});
|
||||
dropLock: z.boolean().optional(),
|
||||
pointsReward: z.number().optional(),
|
||||
}).passthrough();
|
||||
|
||||
const veORNInfoSchema = z.object({
|
||||
result: veORNResultSchema,
|
||||
|
||||
@@ -4,3 +4,4 @@ export * as priceFeed from './PriceFeed/index.js';
|
||||
export * as referralSystem from './ReferralSystem/index.js';
|
||||
export * as frontage from './Frontage';
|
||||
export * as indexer from './Indexer/index.js';
|
||||
export * as frontage from './Frontage/index.js';
|
||||
|
||||
77
src/types.ts
77
src/types.ts
@@ -3,8 +3,9 @@ import type factories from './constants/factories.js';
|
||||
import type { BigNumber } from 'bignumber.js';
|
||||
import type subOrderStatuses from './constants/subOrderStatuses.js';
|
||||
import type positionStatuses from './constants/positionStatuses.js';
|
||||
import type { knownEnvs } from './config/schemas/index.js';
|
||||
import type { knownEnvs } from './config/schemas';
|
||||
import type getHistory from './Orion/bridge/getHistory.js';
|
||||
import type { networkCodes } from './constants';
|
||||
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
@@ -81,23 +82,21 @@ export type Pair = {
|
||||
|
||||
export enum SupportedChainId {
|
||||
MAINNET = '1',
|
||||
ROPSTEN = '3',
|
||||
GOERLI = '5',
|
||||
ARBITRUM = '42161',
|
||||
FANTOM_OPERA = '250',
|
||||
POLYGON = '137',
|
||||
BSC = '56',
|
||||
OKC = '66',
|
||||
POLYGON = '137',
|
||||
OPBNB = '204',
|
||||
FANTOM_OPERA = '250',
|
||||
INEVM = '2525',
|
||||
BASE = '8453',
|
||||
ARBITRUM = '42161',
|
||||
AVAX = '43114',
|
||||
LINEA = '59144',
|
||||
|
||||
POLYGON_TESTNET = '80001',
|
||||
FANTOM_TESTNET = '4002',
|
||||
BSC = '56',
|
||||
BSC_TESTNET = '97',
|
||||
OKC_TESTNET = '65',
|
||||
DRIP_TESTNET = '56303',
|
||||
ARBITRUM_GOERLI = '421613',
|
||||
SEPOLIA = '11155111',
|
||||
EVENT_HORIZON_TESTNET = '123420000034',
|
||||
LUMIA_TESTNET = '1952959480',
|
||||
|
||||
// For testing and debug purpose
|
||||
// BROKEN = '0',
|
||||
@@ -175,6 +174,9 @@ export type SingleSwap = {
|
||||
assetIn: string
|
||||
assetOut: string
|
||||
factory: Factory
|
||||
assetAddressIn: string
|
||||
assetAddressOut: string
|
||||
fee?: number | undefined
|
||||
}
|
||||
|
||||
export type SwapInfoBase = {
|
||||
@@ -208,16 +210,17 @@ export type SwapInfoBase = {
|
||||
marketAmountIn: number | undefined
|
||||
difference: string | undefined
|
||||
} | undefined
|
||||
autoSlippage: number | undefined
|
||||
}
|
||||
|
||||
export type SwapInfoByAmountIn = SwapInfoBase & {
|
||||
kind: 'exactSpend'
|
||||
isTradeBuy: false
|
||||
availableAmountIn?: number | undefined
|
||||
marketAmountOut?: number | undefined
|
||||
}
|
||||
|
||||
export type SwapInfoByAmountOut = SwapInfoBase & {
|
||||
kind: 'exactReceive'
|
||||
isTradeBuy: true
|
||||
marketAmountIn?: number | undefined
|
||||
availableAmountOut?: number | undefined
|
||||
}
|
||||
@@ -283,22 +286,22 @@ export type EnvConfig = {
|
||||
referralAPI: string
|
||||
frontageAPI: string
|
||||
networks: Partial<
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
Record<
|
||||
SupportedChainId,
|
||||
VerboseUnitConfig
|
||||
>
|
||||
>
|
||||
>
|
||||
}
|
||||
export type AggregatedAssets = Partial<
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
Record<
|
||||
string,
|
||||
Partial<
|
||||
Record<SupportedChainId, {
|
||||
address: string
|
||||
}>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
>;
|
||||
|
||||
export type RedeemOrder = {
|
||||
sender: string
|
||||
@@ -438,9 +441,9 @@ type BridgeHistory = Awaited<ReturnType<typeof getHistory>>;
|
||||
type BridgeHistoryItem = NonNullable<BridgeHistory[string]>;
|
||||
|
||||
export type AtomicSwap = Partial<
|
||||
Omit<BridgeHistoryItem, 'creationDate' | 'expiration' | 'secret'>
|
||||
Omit<BridgeHistoryItem, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & Partial<
|
||||
Omit<AtomicSwapLocal, 'creationDate' | 'expiration' | 'secret'>
|
||||
Omit<AtomicSwapLocal, 'creationDate' | 'expiration' | 'secret'>
|
||||
> & {
|
||||
sourceChainId: SupportedChainId
|
||||
targetChainId: SupportedChainId
|
||||
@@ -459,3 +462,21 @@ export type AtomicSwap = Partial<
|
||||
}
|
||||
|
||||
export type OrderSource = 'TERMINAL_MARKET' | 'TERMINAL_LIMIT' | 'SWAP_UI' | 'WIDGET';
|
||||
|
||||
// Frontage
|
||||
export type NetworkCode = typeof networkCodes[number];
|
||||
|
||||
export type TickersCategories = 'FAVORITES' | 'USD' | 'ORN' | 'NATIVE' | 'ALTS';
|
||||
|
||||
export type TickersSortBy = 'PRICE' | 'CHANGE' | 'VOLUME';
|
||||
|
||||
export type TickersSortType = 'ASCENDING' | 'DESCENDING';
|
||||
|
||||
export type TickersBaseSearchParams = {
|
||||
currentNetwork?: NetworkCode
|
||||
targetNetwork?: NetworkCode
|
||||
sortBy?: TickersSortBy
|
||||
sortType?: TickersSortType
|
||||
offset?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
@@ -195,5 +195,5 @@ export async function getTotalBalance(
|
||||
walletBalance,
|
||||
exchangeBalance,
|
||||
totalBalance: walletBalance + exchangeBalance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const swapThroughOrionPoolSchema = z.object({
|
||||
z.bigint(), // amount_spend
|
||||
z.bigint(), // amount_receive
|
||||
z.string().refine(ethers.isAddress).array().nonempty(), // path
|
||||
z.boolean(), // is_exact_spend
|
||||
]),
|
||||
}).transform((data) => ({
|
||||
name: data.name,
|
||||
@@ -16,7 +15,6 @@ const swapThroughOrionPoolSchema = z.object({
|
||||
amount_spend: data.args[0],
|
||||
amount_receive: data.args[1],
|
||||
path: data.args[2],
|
||||
is_exact_spend: data.args[3],
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user