fix: swapMarket and swapLimit

This commit is contained in:
Alex Kraiz
2024-01-04 17:47:53 +04:00
parent 0633f9c113
commit c42e660867
5 changed files with 115 additions and 58 deletions

4
package-lock.json generated
View File

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

View File

@@ -12,6 +12,10 @@ import { signOrder } from '../../crypt/index.js';
import type orderSchema from '../../services/Aggregator/schemas/orderSchema.js';
import type { z } from 'zod';
import { simpleFetch } from 'simple-typed-fetch';
import { generateSwapCalldata } from './generateSwapCalldata.js';
import isValidFactory from '../../utils/isValidFactory.js';
import type { SingleSwap } from '../../types.js';
import { must, safeGet } from '../../utils/safeGetters.js';
export type SwapLimitParams = {
type: 'exactSpend' | 'exactReceive'
@@ -49,6 +53,10 @@ 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 swapLimit({
type,
assetIn,
@@ -85,6 +93,7 @@ export default async function swapLimit({
exchangeContractAddress,
matcherAddress,
assetToAddress,
swapExecutorContractAddress,
} = await simpleFetch(blockchainService.getInfo)();
const nativeCryptocurrency = getNativeCryptocurrencyName(assetToAddress);
@@ -92,7 +101,8 @@ export default async function swapLimit({
const feeAssets = await simpleFetch(blockchainService.getPlatformFees)({ walletAddress, assetIn, assetOut });
const allPrices = await simpleFetch(blockchainService.getPricesWithQuoteAsset)();
const gasPriceWei = await simpleFetch(blockchainService.getGasPriceWei)();
const { factories } = await simpleFetch(blockchainService.getPoolsConfig)();
const { factories, WETHAddress } = await simpleFetch(blockchainService.getPoolsConfig)();
must(WETHAddress, 'WETHAddress is not defined');
const poolExchangesList = factories !== undefined ? Object.keys(factories) : [];
const gasPriceGwei = ethers.formatUnits(gasPriceWei, 'gwei').toString();
@@ -138,7 +148,7 @@ export default async function swapLimit({
: undefined,
);
const { exchanges: swapExchanges } = swapInfo;
const { exchanges: swapExchanges, exchangeContractPath } = swapInfo;
const [firstSwapExchange] = swapExchanges;
@@ -236,12 +246,6 @@ export default async function swapLimit({
if (factoryAddress !== undefined) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`);
}
const pathAddresses = swapInfo.path.map((name) => {
const assetAddress = assetToAddress[name];
if (assetAddress === undefined) throw new Error(`No asset address for ${name}`);
return assetAddress;
});
const amountSpend = swapInfo.type === 'exactSpend'
? swapInfo.amountIn
: new BigNumber(swapInfo.orderInfo.amount).multipliedBy(swapInfo.orderInfo.safePrice)
@@ -271,33 +275,52 @@ export default async function swapLimit({
BigNumber.ROUND_FLOOR,
);
const unsignedSwapThroughOrionPoolTx = await exchangeContract.swapThroughOrionPool.populateTransaction(
amountSpendBlockchainParam,
amountReceiveBlockchainParam,
factoryAddress !== undefined
? [factoryAddress, ...pathAddresses]
: pathAddresses,
type === 'exactSpend',
const { calldata, swapDescription, value } = await generateSwapCalldata({
amount: amountSpendBlockchainParam,
minReturnAmount: amountReceiveBlockchainParam,
path: exchangeContractPath.filter(isValidSingleSwap),
initiatorAddress: walletAddress,
receiverAddress: walletAddress,
provider,
matcher: matcherAddress,
feeToken: feeAssetAddress,
fee: 0,
exchangeContractAddress,
swapExecutorContractAddress,
wethAddress: WETHAddress,
curveRegistryAddress: safeGet(unit.contracts, 'curveRegistry'),
})
const unsignedSwapThroughPoolsTx = await exchangeContract.swap.populateTransaction(
swapExecutorContractAddress,
swapDescription,
new Uint8Array(0),
calldata,
{
value
}
);
unsignedSwapThroughOrionPoolTx.chainId = BigInt(parseInt(chainId, 10));
unsignedSwapThroughOrionPoolTx.gasPrice = BigInt(gasPriceWei);
unsignedSwapThroughPoolsTx.chainId = BigInt(parseInt(chainId, 10));
unsignedSwapThroughPoolsTx.gasPrice = BigInt(gasPriceWei);
unsignedSwapThroughOrionPoolTx.from = walletAddress;
unsignedSwapThroughPoolsTx.from = walletAddress;
const amountSpendBN = new BigNumber(amountSpend);
let value = new BigNumber(0);
let txValue = new BigNumber(0);
const denormalizedAssetInExchangeBalance = balances[assetIn]?.exchange;
if (denormalizedAssetInExchangeBalance === undefined) throw new Error(`Asset '${assetIn}' exchange balance is not found`);
if (assetIn === nativeCryptocurrency && amountSpendBN.gt(denormalizedAssetInExchangeBalance)) {
value = amountSpendBN.minus(denormalizedAssetInExchangeBalance);
txValue = amountSpendBN.minus(denormalizedAssetInExchangeBalance);
}
unsignedSwapThroughOrionPoolTx.value = normalizeNumber(
value.dp(INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_CEIL),
unsignedSwapThroughPoolsTx.value = normalizeNumber(
txValue.dp(INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_CEIL),
NATIVE_CURRENCY_PRECISION,
BigNumber.ROUND_CEIL,
);
unsignedSwapThroughOrionPoolTx.gasLimit = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT);
unsignedSwapThroughPoolsTx.gasLimit = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT);
const transactionCost = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT) * BigInt(gasPriceWei);
const denormalizedTransactionCost = denormalizeNumber(transactionCost, BigInt(NATIVE_CURRENCY_PRECISION));
@@ -327,10 +350,10 @@ export default async function swapLimit({
await balanceGuard.check(options?.autoApprove);
const nonce = await provider.getTransactionCount(walletAddress, 'pending');
unsignedSwapThroughOrionPoolTx.nonce = nonce;
unsignedSwapThroughPoolsTx.nonce = nonce;
options?.logger?.('Signing transaction...');
const swapThroughOrionPoolTxResponse = await signer.sendTransaction(unsignedSwapThroughOrionPoolTx);
const swapThroughOrionPoolTxResponse = await signer.sendTransaction(unsignedSwapThroughPoolsTx);
options?.logger?.(`Transaction sent. Tx hash: ${swapThroughOrionPoolTxResponse.hash}`);
return {
amountOut: swapInfo.amountOut,

View File

@@ -12,6 +12,10 @@ import type orderSchema from '../../services/Aggregator/schemas/orderSchema.js';
import type { z } from 'zod';
import type { SwapLimitParams } from './swapLimit.js';
import { simpleFetch } from 'simple-typed-fetch';
import { generateSwapCalldata } from './generateSwapCalldata.js';
import isValidFactory from '../../utils/isValidFactory.js';
import type { SingleSwap } from '../../types.js';
import { must, safeGet } from '../../utils/safeGetters.js';
export type SwapMarketParams = Omit<SwapLimitParams, 'price'> & {
slippagePercent: BigNumber.Value
@@ -33,6 +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,
@@ -71,6 +80,7 @@ export default async function swapMarket({
exchangeContractAddress,
matcherAddress,
assetToAddress,
swapExecutorContractAddress,
} = await simpleFetch(blockchainService.getInfo)();
const nativeCryptocurrency = getNativeCryptocurrencyName(assetToAddress);
@@ -78,7 +88,8 @@ export default async function swapMarket({
const feeAssets = await simpleFetch(blockchainService.getPlatformFees)({ walletAddress, assetIn, assetOut });
const allPrices = await simpleFetch(blockchainService.getPricesWithQuoteAsset)();
const gasPriceWei = await simpleFetch(blockchainService.getGasPriceWei)();
const { factories } = await simpleFetch(blockchainService.getPoolsConfig)();
const { factories, WETHAddress } = await simpleFetch(blockchainService.getPoolsConfig)();
must(WETHAddress, 'WETHAddress is not defined');
const poolExchangesList = factories !== undefined ? Object.keys(factories) : [];
const gasPriceGwei = ethers.formatUnits(gasPriceWei, 'gwei').toString();
@@ -124,7 +135,7 @@ export default async function swapMarket({
: undefined,
);
const { exchanges: swapExchanges } = swapInfo;
const { exchanges: swapExchanges, exchangeContractPath } = swapInfo;
const [firstSwapExchange] = swapExchanges;
@@ -185,12 +196,6 @@ export default async function swapMarket({
if (factoryAddress !== undefined) options?.logger?.(`Factory address is ${factoryAddress}. Exchange is ${firstSwapExchange}`);
}
const pathAddresses = swapInfo.path.map((name) => {
const assetAddress = assetToAddress[name];
if (assetAddress === undefined) throw new Error(`No asset address for ${name}`);
return assetAddress;
});
const amountOutWithSlippage = new BigNumber(swapInfo.amountOut)
.multipliedBy(new BigNumber(1).minus(percent))
.toString();
@@ -222,33 +227,52 @@ export default async function swapMarket({
INTERNAL_PROTOCOL_PRECISION,
BigNumber.ROUND_FLOOR,
);
const unsignedSwapThroughOrionPoolTx = await exchangeContract.swapThroughOrionPool.populateTransaction(
amountSpendBlockchainParam.toString(),
amountReceiveBlockchainParam.toString(),
factoryAddress !== undefined
? [factoryAddress, ...pathAddresses]
: pathAddresses,
type === 'exactSpend',
const { calldata, swapDescription, value } = await generateSwapCalldata({
amount: amountSpendBlockchainParam,
minReturnAmount: amountReceiveBlockchainParam,
path: exchangeContractPath.filter(isValidSingleSwap),
initiatorAddress: walletAddress,
receiverAddress: walletAddress,
provider,
matcher: matcherAddress,
feeToken: feeAssetAddress,
fee: 0,
exchangeContractAddress,
swapExecutorContractAddress,
wethAddress: WETHAddress,
curveRegistryAddress: safeGet(unit.contracts, 'curveRegistry'),
})
const unsignedSwapThroughPoolsTx = await exchangeContract.swap.populateTransaction(
swapExecutorContractAddress,
swapDescription,
new Uint8Array(0),
calldata,
{
value
}
);
unsignedSwapThroughOrionPoolTx.chainId = BigInt(chainId);
unsignedSwapThroughOrionPoolTx.gasPrice = BigInt(gasPriceWei);
unsignedSwapThroughPoolsTx.chainId = BigInt(parseInt(chainId, 10));
unsignedSwapThroughPoolsTx.gasPrice = BigInt(gasPriceWei);
unsignedSwapThroughOrionPoolTx.from = walletAddress;
unsignedSwapThroughPoolsTx.from = walletAddress;
const amountSpendBN = new BigNumber(amountSpend);
let value = new BigNumber(0);
let txValue = new BigNumber(0);
const denormalizedAssetInExchangeBalance = balances[assetIn]?.exchange;
if (denormalizedAssetInExchangeBalance === undefined) throw new Error(`Asset '${assetIn}' exchange balance is not found`);
if (assetIn === nativeCryptocurrency && amountSpendBN.gt(denormalizedAssetInExchangeBalance)) {
value = amountSpendBN.minus(denormalizedAssetInExchangeBalance);
txValue = amountSpendBN.minus(denormalizedAssetInExchangeBalance);
}
unsignedSwapThroughOrionPoolTx.value = normalizeNumber(
value.dp(INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_CEIL),
unsignedSwapThroughPoolsTx.value = normalizeNumber(
txValue.dp(INTERNAL_PROTOCOL_PRECISION, BigNumber.ROUND_CEIL),
NATIVE_CURRENCY_PRECISION,
BigNumber.ROUND_CEIL,
);
unsignedSwapThroughOrionPoolTx.gasLimit = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT);
unsignedSwapThroughPoolsTx.gasLimit = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT);
const transactionCost = BigInt(SWAP_THROUGH_ORION_POOL_GAS_LIMIT) * BigInt(gasPriceWei);
const denormalizedTransactionCost = denormalizeNumber(transactionCost, BigInt(NATIVE_CURRENCY_PRECISION));
@@ -278,10 +302,10 @@ export default async function swapMarket({
await balanceGuard.check(options?.autoApprove);
const nonce = await provider.getTransactionCount(walletAddress, 'pending');
unsignedSwapThroughOrionPoolTx.nonce = nonce;
unsignedSwapThroughPoolsTx.nonce = nonce;
options?.logger?.('Signing transaction...');
const swapThroughOrionPoolTxResponse = await signer.sendTransaction(unsignedSwapThroughOrionPoolTx);
const swapThroughOrionPoolTxResponse = await signer.sendTransaction(unsignedSwapThroughPoolsTx);
options?.logger?.(`Transaction sent. Tx hash: ${swapThroughOrionPoolTxResponse.hash}`);
return {
amountOut: swapInfo.amountOut,

View File

@@ -7,6 +7,13 @@ const orderInfoSchema = z.object({
safePrice: z.number(),
}).nullable();
const exchangeContractStep = z.object({
pool: z.string(),
assetIn: z.string(),
assetOut: z.string(),
factory: z.string(),
});
const swapInfoBase = z.object({
id: z.string(),
amountIn: z.number(),
@@ -22,12 +29,7 @@ const swapInfoBase = z.object({
minAmountOut: z.number(),
minAmountIn: z.number(),
marketPrice: z.number().nullable(), // spending asset market price
exchangeContractPath: z.array(z.object({
pool: z.string(),
assetIn: z.string(),
assetOut: z.string(),
factory: z.string(),
})),
exchangeContractPath: z.array(exchangeContractStep),
alternatives: z.object({ // execution alternatives
exchanges: z.array(z.string()),
path: z.array(z.string()),

View File

@@ -0,0 +1,8 @@
import { type Factory } from '../types.js';
import { factories } from '../index.js';
const isValidFactory = (factory: string): factory is Factory => {
return factories.some((f) => f === factory);
};
export default isValidFactory;