Merge pull request #250 from orionprotocol/generateSwapCallData-fix

OP-5228: generateSwapCallData fix
This commit is contained in:
Mikhail Gladchenko
2024-05-27 15:26:34 +01:00
committed by GitHub
9 changed files with 128 additions and 80 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@orionprotocol/sdk", "name": "@orionprotocol/sdk",
"version": "0.20.86", "version": "0.20.76-rc110",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@orionprotocol/sdk", "name": "@orionprotocol/sdk",
"version": "0.20.86", "version": "0.20.76-rc110",
"hasInstallScript": true, "hasInstallScript": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@orionprotocol/sdk", "name": "@orionprotocol/sdk",
"version": "0.21.1", "version": "0.20.76-rc115",
"description": "Orion Protocol SDK", "description": "Orion Protocol SDK",
"main": "./lib/index.cjs", "main": "./lib/index.cjs",
"module": "./lib/index.js", "module": "./lib/index.js",

View File

@@ -3,6 +3,15 @@ import { SafeArray } from "../../../utils/safeGetters.js"
import { type BytesLike, type BigNumberish, concat, ethers, toBeHex } from "ethers" import { type BytesLike, type BigNumberish, concat, ethers, toBeHex } from "ethers"
import { addCallParams } from "./utils.js" import { addCallParams } from "./utils.js"
import type { SingleSwap } from "../../../types.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( export async function generateUni2Calls(
path: SafeArray<SingleSwap>, path: SafeArray<SingleSwap>,
@@ -19,17 +28,21 @@ export async function generateUni2Calls(
currentSwap.pool, currentSwap.pool,
currentSwap.assetIn, currentSwap.assetIn,
currentSwap.assetOut, currentSwap.assetOut,
nextSwap.pool nextSwap.pool,
currentSwap.fee
) )
calls.push(call) calls.push(call)
} }
} }
const lastSwap = path.last(); const lastSwap = path.last();
const calldata = executorInterface.encodeFunctionData('swapUniV2', [ const fee = lastSwap.fee ?? 3;
const scaledFee = countScaledFee(fee.toString());
const calldata = executorInterface.encodeFunctionData('swapUniV2Scaled', [
lastSwap.pool, lastSwap.pool,
lastSwap.assetIn, lastSwap.assetIn,
lastSwap.assetOut, lastSwap.assetOut,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat(['0x03', recipient])]), ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(scaledFee), recipient])]),
]) ])
calls.push(addCallParams(calldata)) calls.push(addCallParams(calldata))
@@ -44,11 +57,12 @@ export function generateUni2Call(
fee: BigNumberish = 3, fee: BigNumberish = 3,
) { ) {
const executorInterface = SwapExecutor__factory.createInterface() const executorInterface = SwapExecutor__factory.createInterface()
const calldata = executorInterface.encodeFunctionData('swapUniV2', [ const scaledFee = countScaledFee(fee.toString());
const calldata = executorInterface.encodeFunctionData('swapUniV2Scaled', [
pool, pool,
assetIn, assetIn,
assetOut, assetOut,
ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(fee), recipient])]), ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [concat([toBeHex(scaledFee), recipient])]),
]) ])
return addCallParams(calldata) return addCallParams(calldata)
} }

View File

@@ -1,49 +1,50 @@
import type { LibValidator } from "@orionprotocol/contracts/lib/ethers-v6/Exchange.js"; import type { LibValidator } from '@orionprotocol/contracts/lib/ethers-v6/Exchange.js';
import { ethers, ZeroAddress } from "ethers"; import { ethers, ZeroAddress } from 'ethers';
import type { AddressLike, JsonRpcProvider, BigNumberish, BytesLike } from "ethers"; import type { AddressLike, JsonRpcProvider, BigNumberish, BytesLike } from 'ethers';
import cloneDeep from "lodash.clonedeep"; import cloneDeep from 'lodash.clonedeep';
import { safeGet, SafeArray } from "../../utils/safeGetters.js"; import { safeGet, SafeArray } from '../../utils/safeGetters.js';
import { simpleFetch } from "simple-typed-fetch"; import { simpleFetch } from 'simple-typed-fetch';
import type Unit from "../index.js"; import type Unit from '../index.js';
import { generateUni2Calls, generateUni2Call } from "./callGenerators/uniswapV2.js"; import { generateUni2Calls, generateUni2Call } from './callGenerators/uniswapV2.js';
import { import {
generateUni3Calls, generateUni3Calls,
generateOrion3Calls, generateOrion3Calls,
generateUni3Call, generateUni3Call,
generateOrion3Call, generateOrion3Call,
} from "./callGenerators/uniswapV3.js"; } from './callGenerators/uniswapV3.js';
import { exchangeToNativeDecimals, generateCalls, pathCallWithBalance } from "./callGenerators/utils.js"; import { exchangeToNativeDecimals, generateCalls, pathCallWithBalance } from './callGenerators/utils.js';
import { generateTransferCall } from "./callGenerators/erc20.js"; import { generateTransferCall } from './callGenerators/erc20.js';
import { generateCurveStableSwapCall } from "./callGenerators/curve.js"; import { generateCurveStableSwapCall } from './callGenerators/curve.js';
import type { SingleSwap } from "../../types.js"; import type { SingleSwap } from '../../types.js';
import { addressLikeToString } from "../../utils/addressLikeToString.js"; import { addressLikeToString } from '../../utils/addressLikeToString.js';
import { generateUnwrapAndTransferCall, generateWrapAndTransferCall } from "./callGenerators/weth.js"; import { generateUnwrapAndTransferCall, generateWrapAndTransferCall } from './callGenerators/weth.js';
import { getExchangeAllowance, getTotalBalance } from "../../utils/getBalance.js"; import { getExchangeAllowance, getTotalBalance } from '../../utils/getBalance.js';
import { generateFeePaymentCall } from "./callGenerators/feePayment.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 = { type BaseGenerateSwapCalldataParams = {
amount: BigNumberish; amount: BigNumberish
minReturnAmount: BigNumberish; minReturnAmount: BigNumberish
initiatorAddress: string; initiatorAddress: string
receiverAddress: string; receiverAddress: string
path: ArrayLike<SingleSwap>; path: ArrayLike<SingleSwap>
matcher?: AddressLike, matcher?: AddressLike
feeToken?: AddressLike, feeToken?: AddressLike
fee?: BigNumberish; fee?: BigNumberish
} }
export type GenerateSwapCalldataWithUnitParams = BaseGenerateSwapCalldataParams & { export type GenerateSwapCalldataWithUnitParams = BaseGenerateSwapCalldataParams & {
unit: Unit; unit: Unit
}; };
export type GenerateSwapCalldataParams = BaseGenerateSwapCalldataParams & { export type GenerateSwapCalldataParams = BaseGenerateSwapCalldataParams & {
exchangeContractAddress: AddressLike; exchangeContractAddress: AddressLike
wethAddress: AddressLike; wethAddress: AddressLike
curveRegistryAddress: AddressLike; curveRegistryAddress: AddressLike
swapExecutorContractAddress: AddressLike; swapExecutorContractAddress: AddressLike
provider: JsonRpcProvider; provider: JsonRpcProvider
logger?: ((message: string) => void) | undefined
}; };
export async function generateSwapCalldataWithUnit({ export async function generateSwapCalldataWithUnit({
@@ -57,29 +58,27 @@ export async function generateSwapCalldataWithUnit({
fee = 0, fee = 0,
unit, unit,
}: GenerateSwapCalldataWithUnitParams): Promise<{ }: GenerateSwapCalldataWithUnitParams): Promise<{
calldata: string; calldata: string
swapDescription: LibValidator.SwapDescriptionStruct; swapDescription: LibValidator.SwapDescriptionStruct
value: bigint; value: bigint
}> { }> {
if (arrayLikePath == undefined || arrayLikePath.length == 0) { if (arrayLikePath == undefined || arrayLikePath.length == 0) {
throw new Error("Empty path"); throw new Error('Empty path');
} }
const wethAddress = safeGet(unit.contracts, "WETH"); const wethAddress = safeGet(unit.contracts, 'WETH');
const curveRegistryAddress = safeGet(unit.contracts, "curveRegistry"); const curveRegistryAddress = safeGet(unit.contracts, 'curveRegistry');
const { assetToAddress, swapExecutorContractAddress, exchangeContractAddress } = await simpleFetch( const { swapExecutorContractAddress, exchangeContractAddress } = await simpleFetch(
unit.blockchainService.getInfo unit.blockchainService.getInfo
)(); )();
const arrayLikePathCopy = cloneDeep(arrayLikePath); const arrayLikePathCopy = cloneDeep(arrayLikePath);
let path = SafeArray.from(arrayLikePathCopy); let path = SafeArray.from(arrayLikePathCopy);
path = SafeArray.from(arrayLikePathCopy).map((swapInfo) => { path = SafeArray.from(arrayLikePathCopy).map((swapInfo) => ({
swapInfo.assetIn = assetToAddress[swapInfo.assetIn] ?? swapInfo.assetIn ...swapInfo,
swapInfo.assetOut = assetToAddress[swapInfo.assetOut] ?? swapInfo.assetOut assetIn: swapInfo.assetAddressIn.toLowerCase(),
swapInfo.assetIn = swapInfo.assetIn.toLowerCase() assetOut: swapInfo.assetAddressOut.toLowerCase(),
swapInfo.assetOut = swapInfo.assetOut.toLowerCase() }));
return swapInfo;
});
return await generateSwapCalldata({ return await generateSwapCalldata({
amount, amount,
@@ -95,6 +94,7 @@ export async function generateSwapCalldataWithUnit({
curveRegistryAddress, curveRegistryAddress,
swapExecutorContractAddress, swapExecutorContractAddress,
provider: unit.provider, provider: unit.provider,
logger: unit.logger,
}); });
} }
@@ -112,21 +112,30 @@ export async function generateSwapCalldata({
curveRegistryAddress: curveRegistryAddressLike, curveRegistryAddress: curveRegistryAddressLike,
swapExecutorContractAddress: swapExecutorContractAddressLike, swapExecutorContractAddress: swapExecutorContractAddressLike,
provider, provider,
logger,
}: GenerateSwapCalldataParams): Promise<{ }: GenerateSwapCalldataParams): Promise<{
calldata: string; calldata: string
swapDescription: LibValidator.SwapDescriptionStruct; swapDescription: LibValidator.SwapDescriptionStruct
value: bigint; value: bigint
}> { }> {
const wethAddress = await addressLikeToString(wethAddressLike); const wethAddress = await addressLikeToString(wethAddressLike);
logger?.(`wethAddress: ${wethAddress}`);
const curveRegistryAddress = await addressLikeToString(curveRegistryAddressLike); const curveRegistryAddress = await addressLikeToString(curveRegistryAddressLike);
logger?.(`curveRegistryAddress: ${curveRegistryAddress}`);
const swapExecutorContractAddress = await addressLikeToString(swapExecutorContractAddressLike); const swapExecutorContractAddress = await addressLikeToString(swapExecutorContractAddressLike);
logger?.(`swapExecutorContractAddress, ${swapExecutorContractAddress}`);
const feeToken = await addressLikeToString(feeTokenAddressLike); const feeToken = await addressLikeToString(feeTokenAddressLike);
logger?.(`feeToken, ${feeToken}`);
const matcher = await addressLikeToString(matcherAddressLike); const matcher = await addressLikeToString(matcherAddressLike);
logger?.(`matcher: ${matcher}`);
logger?.(`arrayLikePath: ${arrayLikePath}`);
let path = SafeArray.from(arrayLikePath).map((swapInfo) => { let path = SafeArray.from(arrayLikePath).map((swapInfo) => {
logger?.(`swapInfo: ${swapInfo}`);
swapInfo.assetIn = swapInfo.assetIn.toLowerCase() swapInfo.assetIn = swapInfo.assetIn.toLowerCase()
swapInfo.assetOut = swapInfo.assetOut.toLowerCase() swapInfo.assetOut = swapInfo.assetOut.toLowerCase()
return swapInfo; return swapInfo;
}); });
logger?.(`path: ${path}`);
const { assetIn: srcToken } = path.first(); const { assetIn: srcToken } = path.first();
const { assetOut: dstToken } = path.last(); const { assetOut: dstToken } = path.last();
@@ -140,14 +149,18 @@ export async function generateSwapCalldata({
minReturnAmount, minReturnAmount,
flags: 0, flags: 0,
}; };
logger?.(`swapDescription: ${swapDescription}`);
const amountNativeDecimals = await exchangeToNativeDecimals(srcToken, amount, provider); const amountNativeDecimals = await exchangeToNativeDecimals(srcToken, amount, provider);
logger?.(`amountNativeDecimals: ${amountNativeDecimals}`);
const feeNativeDecimals = await exchangeToNativeDecimals(feeToken, fee, provider) const feeNativeDecimals = await exchangeToNativeDecimals(feeToken, fee, provider)
logger?.(`feeNativeDecimals: ${feeNativeDecimals}`);
path = SafeArray.from(arrayLikePath).map((singleSwap) => { path = SafeArray.from(arrayLikePath).map((singleSwap) => {
if (singleSwap.assetIn == ethers.ZeroAddress) singleSwap.assetIn = wethAddress; if (singleSwap.assetIn == ethers.ZeroAddress) singleSwap.assetIn = wethAddress;
if (singleSwap.assetOut == ethers.ZeroAddress) singleSwap.assetOut = wethAddress; if (singleSwap.assetOut == ethers.ZeroAddress) singleSwap.assetOut = wethAddress;
return singleSwap; return singleSwap;
}); });
logger?.(`path2: ${path}`);
let calls: BytesLike[]; let calls: BytesLike[];
({ swapDescription, calls } = await processSwaps( ({ swapDescription, calls } = await processSwaps(
@@ -162,19 +175,26 @@ export async function generateSwapCalldata({
curveRegistryAddress, curveRegistryAddress,
provider provider
)); ));
logger?.(`swapDescription: ${swapDescription}`);
logger?.(`calls: ${calls}`);
const calldata = generateCalls(calls); const calldata = generateCalls(calls);
logger?.(`calldata: ${calldata}`);
const { useExchangeBalance, additionalTransferAmount } = await shouldUseExchangeBalance( const { useExchangeBalance, additionalTransferAmount } = await shouldUseExchangeBalance(
srcToken, srcToken,
initiatorAddress, initiatorAddress,
exchangeContractAddress, exchangeContractAddress,
amountNativeDecimals, amountNativeDecimals,
provider provider,
logger
); );
logger?.(`useExchangeBalance: ${useExchangeBalance}`);
logger?.(`additionalTransferAmount: ${additionalTransferAmount}`);
if (useExchangeBalance) { if (useExchangeBalance) {
swapDescription.flags = 1n << 255n; swapDescription.flags = 1n << 255n;
} }
const value = srcToken == ZeroAddress ? additionalTransferAmount : 0n; const value = srcToken == ZeroAddress ? additionalTransferAmount : 0n;
logger?.(`value: ${value}`);
return { swapDescription, calldata, 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( ({ swapDescription, calls } = wrapOrUnwrapIfNeeded(
amount, amount,
@@ -238,27 +258,27 @@ async function processSingleFactorySwaps(
) { ) {
let calls: BytesLike[] = []; let calls: BytesLike[] = [];
switch (factory) { switch (factory) {
case "OrionV2": { case 'OrionV2': {
swapDescription.srcReceiver = path.first().pool; swapDescription.srcReceiver = path.first().pool;
calls = await generateUni2Calls(path, swapExecutorContractAddress); calls = await generateUni2Calls(path, swapExecutorContractAddress);
break; break;
} }
case "UniswapV2": { case 'UniswapV2': {
swapDescription.srcReceiver = path.first().pool; swapDescription.srcReceiver = path.first().pool;
calls = await generateUni2Calls(path, swapExecutorContractAddress); calls = await generateUni2Calls(path, swapExecutorContractAddress);
break; break;
} }
case "UniswapV3": { case 'UniswapV3': {
calls = await generateUni3Calls(path, amount, swapExecutorContractAddress, provider); calls = await generateUni3Calls(path, amount, swapExecutorContractAddress, provider);
break; break;
} }
case "OrionV3": { case 'OrionV3': {
calls = await generateOrion3Calls(path, amount, swapExecutorContractAddress, provider); calls = await generateOrion3Calls(path, amount, swapExecutorContractAddress, provider);
break; break;
} }
case "Curve": { case 'Curve': {
if (path.length > 1) { 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( calls = await generateCurveStableSwapCall(
amount, amount,
@@ -285,37 +305,37 @@ async function processMultiFactorySwaps(
curveRegistryAddress: string, curveRegistryAddress: string,
provider: JsonRpcProvider provider: JsonRpcProvider
) { ) {
let calls: BytesLike[] = []; const calls: BytesLike[] = [];
for (const swap of path) { for (const swap of path) {
switch (swap.factory) { switch (swap.factory) {
case "OrionV2": { case 'OrionV2': {
let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0); let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0);
transferCall = pathCallWithBalance(transferCall, swap.assetIn); 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); calls.push(transferCall, uni2Call);
break; break;
} }
case "UniswapV2": { case 'UniswapV2': {
let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0); let transferCall = generateTransferCall(swap.assetIn, swap.pool, 0);
transferCall = pathCallWithBalance(transferCall, swap.assetIn); 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); calls.push(transferCall, uni2Call);
break; break;
} }
case "UniswapV3": { case 'UniswapV3': {
let uni3Call = await generateUni3Call(swap, 0, swapExecutorContractAddress, provider); let uni3Call = await generateUni3Call(swap, 0, swapExecutorContractAddress, provider);
uni3Call = pathCallWithBalance(uni3Call, swap.assetIn); uni3Call = pathCallWithBalance(uni3Call, swap.assetIn);
calls.push(uni3Call); calls.push(uni3Call);
break; break;
} }
case "OrionV3": { case 'OrionV3': {
let orion3Call = await generateOrion3Call(swap, 0, swapExecutorContractAddress, provider); let orion3Call = await generateOrion3Call(swap, 0, swapExecutorContractAddress, provider);
orion3Call = pathCallWithBalance(orion3Call, swap.assetIn); orion3Call = pathCallWithBalance(orion3Call, swap.assetIn);
calls.push(orion3Call); calls.push(orion3Call);
break; break;
} }
case "Curve": { case 'Curve': {
let curveCalls = await generateCurveStableSwapCall( const curveCalls = await generateCurveStableSwapCall(
amount, amount,
swapExecutorContractAddress, swapExecutorContractAddress,
swap, swap,
@@ -346,7 +366,7 @@ async function payFeeToMatcher(
const feePaymentCall = generateFeePaymentCall(matcher, feeToken, feeAmount) const feePaymentCall = generateFeePaymentCall(matcher, feeToken, feeAmount)
calls.push(feePaymentCall) calls.push(feePaymentCall)
} }
return {swapDescription, calls} return { swapDescription, calls }
} }
function wrapOrUnwrapIfNeeded( function wrapOrUnwrapIfNeeded(
@@ -356,7 +376,7 @@ function wrapOrUnwrapIfNeeded(
swapExecutorContractAddress: string, swapExecutorContractAddress: string,
wethAddress: string wethAddress: string
) { ) {
const {dstReceiver, srcReceiver, srcToken, dstToken} = swapDescription; const { dstReceiver, srcReceiver, srcToken, dstToken } = swapDescription;
if (srcToken === ZeroAddress) { if (srcToken === ZeroAddress) {
const wrapCall = generateWrapAndTransferCall(srcReceiver, { value: amount }); const wrapCall = generateWrapAndTransferCall(srcReceiver, { value: amount });
swapDescription.srcReceiver = swapExecutorContractAddress; swapDescription.srcReceiver = swapExecutorContractAddress;
@@ -379,7 +399,8 @@ async function shouldUseExchangeBalance(
initiatorAddress: AddressLike, initiatorAddress: AddressLike,
exchangeContractAddress: AddressLike, exchangeContractAddress: AddressLike,
amount: bigint, amount: bigint,
provider: JsonRpcProvider provider: JsonRpcProvider,
logger?: ((message: string) => void) | undefined
) { ) {
const { walletBalance, exchangeBalance } = await getTotalBalance( const { walletBalance, exchangeBalance } = await getTotalBalance(
srcToken, srcToken,
@@ -387,11 +408,13 @@ async function shouldUseExchangeBalance(
exchangeContractAddress, exchangeContractAddress,
provider provider
); );
const exchangeAllowance = await getExchangeAllowance(srcToken, initiatorAddress, exchangeContractAddress, provider); const exchangeAllowance = await getExchangeAllowance(srcToken, initiatorAddress, exchangeContractAddress, provider);
logger?.('test_123');
if (walletBalance + exchangeBalance < amount) { if (walletBalance + exchangeBalance < amount) {
throw new Error( 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 useExchangeBalance = true;

View File

@@ -47,7 +47,7 @@ export default class Exchange {
public generateSwapCalldata(params: PureGenerateSwapCalldataParams) { public generateSwapCalldata(params: PureGenerateSwapCalldataParams) {
return generateSwapCalldataWithUnit({ return generateSwapCalldataWithUnit({
...params, ...params,
unit: this.unit unit: this.unit,
}) })
} }

View File

@@ -12,6 +12,8 @@ const exchangeContractStep = z.object({
assetIn: z.string(), assetIn: z.string(),
assetOut: z.string(), assetOut: z.string(),
factory: z.string(), factory: z.string(),
assetAddressIn: z.string(),
assetAddressOut: z.string(),
}); });
const swapInfoBase = z.object({ const swapInfoBase = z.object({

View File

@@ -514,6 +514,9 @@ class AggregatorWS {
assetIn: path.ai, assetIn: path.ai,
assetOut: path.ao, assetOut: path.ao,
factory: path.f, factory: path.f,
assetAddressIn: path.aai,
assetAddressOut: path.aao,
fee: path.fee,
})), })),
poolOptimal: json.po, poolOptimal: json.po,
...(json.oi) && { ...(json.oi) && {

View File

@@ -40,6 +40,9 @@ const swapInfoSchemaBase = baseMessageSchema.extend({
ai: z.string().toUpperCase(), // asset in ai: z.string().toUpperCase(), // asset in
ao: z.string().toUpperCase(), // asset out ao: z.string().toUpperCase(), // asset out
f: factorySchema, // factory 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 usd: z.object({ // USD info of this swap, nullable
aa: z.number().optional(), // available amount in, USD aa: z.number().optional(), // available amount in, USD

View File

@@ -179,6 +179,9 @@ export type SingleSwap = {
assetIn: string assetIn: string
assetOut: string assetOut: string
factory: Factory factory: Factory
assetAddressIn: string
assetAddressOut: string
fee?: number | undefined
} }
export type SwapInfoBase = { export type SwapInfoBase = {