|
|
|
|
@@ -1,49 +1,50 @@
|
|
|
|
|
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';
|
|
|
|
|
|
|
|
|
|
export type Factory = "UniswapV2" | "UniswapV3" | "Curve" | "OrionV2" | "OrionV3";
|
|
|
|
|
export type Factory = 'UniswapV2' | 'UniswapV3' | 'Curve' | 'OrionV2' | 'OrionV3';
|
|
|
|
|
|
|
|
|
|
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 +58,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 +94,7 @@ export async function generateSwapCalldataWithUnit({
|
|
|
|
|
curveRegistryAddress,
|
|
|
|
|
swapExecutorContractAddress,
|
|
|
|
|
provider: unit.provider,
|
|
|
|
|
logger: unit.logger,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -112,21 +112,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 +149,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 +175,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 +234,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 +258,27 @@ 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 '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 +305,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 +366,7 @@ async function payFeeToMatcher(
|
|
|
|
|
const feePaymentCall = generateFeePaymentCall(matcher, feeToken, feeAmount)
|
|
|
|
|
calls.push(feePaymentCall)
|
|
|
|
|
}
|
|
|
|
|
return {swapDescription, calls}
|
|
|
|
|
return { swapDescription, calls }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function wrapOrUnwrapIfNeeded(
|
|
|
|
|
@@ -356,7 +376,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 +399,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,11 +408,13 @@ 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;
|
|
|
|
|
|