Skip to content

Commit

Permalink
Merge pull request paraswap#466 from paraswap/fix/1222-multihop-buy-b…
Browse files Browse the repository at this point in the history
…alancer

BACK-1222: fix BalancerV2 buy multihop
  • Loading branch information
Verisana authored Aug 15, 2023
2 parents 3ff94ef + c4a402e commit bcda72d
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@paraswap/dex-lib",
"version": "2.28.7",
"version": "2.28.8",
"main": "build/index.js",
"types": "build/index.d.ts",
"repository": "https://github.com/paraswap/paraswap-dex-lib",
Expand Down
2 changes: 2 additions & 0 deletions src/dex/balancer-v2/balancer-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ export class BalancerV2
prices: resOut.prices,
data: {
poolId: pool.id,
tokenIn: _from.address.toLowerCase(),
tokenOut: _to.address.toLowerCase(),
},
poolAddresses: [poolAddress],
exchange: this.dexKey,
Expand Down
111 changes: 62 additions & 49 deletions src/dex/balancer-v2/optimizer.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,83 @@
import { UnoptimizedRate, OptimalSwapExchange } from '../../types';
import { BalancerSwapV2 } from './types';
import _ from 'lodash';
import { UnoptimizedRate } from '../../types';
import { SwapSide } from '../../constants';
import { BalancerConfig } from './config';

const MAX_UINT256 =
'115792089237316195423570985008687907853269984665640564039457584007913129639935';

const AllBalancerV2Forks = Object.keys(BalancerConfig);
import { OptimalSwap } from '@paraswap/core';

export function balancerV2Merge(or: UnoptimizedRate): UnoptimizedRate {
const fixSwap = (
rawSwap: OptimalSwapExchange<any>[],
side: SwapSide,
): OptimalSwapExchange<any>[] => {
const newBalancers: { [key: string]: OptimalSwapExchange<any> } = {};
let optimizedSwap = new Array<OptimalSwapExchange<any>>();
rawSwap.forEach((s: OptimalSwapExchange<any>) => {
const exchangeKey = s.exchange.toLowerCase();
if (AllBalancerV2Forks.some(d => d.toLowerCase() === exchangeKey)) {
if (!(exchangeKey in newBalancers)) {
newBalancers[exchangeKey] = {
exchange: s.exchange,
srcAmount: '0',
destAmount: '0',
percent: 0,
poolAddresses: [],
data: {
swaps: new Array<BalancerSwapV2>(),
gasUSD: '0',
},
};
}
newBalancers[exchangeKey].srcAmount = (
BigInt(newBalancers[exchangeKey].srcAmount) + BigInt(s.srcAmount)
const balancerForksList = Object.keys(BalancerConfig).map(b =>
b.toLowerCase(),
);
const fixSwap = (rawRate: OptimalSwap[], side: SwapSide): OptimalSwap[] => {
let lastExchange: false | OptimalSwap = false;
let optimizedRate = new Array<OptimalSwap>();
rawRate.forEach((s: OptimalSwap) => {
if (
s.swapExchanges.length !== 1 ||
!balancerForksList.includes(s.swapExchanges[0].exchange.toLowerCase())
) {
lastExchange = false;
optimizedRate.push(s);
} else if (
lastExchange &&
lastExchange.swapExchanges[0].exchange.toLowerCase() ===
s.swapExchanges[0].exchange.toLowerCase() &&
_.last(
<any[]>lastExchange.swapExchanges[0].data.swaps,
)!.tokenOut.toLowerCase() ===
s.swapExchanges[0].data.tokenIn.toLowerCase()
) {
const [lastExchangeSwap] = lastExchange.swapExchanges;
const [currentSwap] = s.swapExchanges;
lastExchangeSwap.srcAmount = (
BigInt(lastExchangeSwap.srcAmount) + BigInt(currentSwap.srcAmount)
).toString();

newBalancers[exchangeKey].destAmount = (
BigInt(newBalancers[exchangeKey].destAmount) + BigInt(s.destAmount)
lastExchangeSwap.destAmount = (
BigInt(lastExchangeSwap.destAmount) + BigInt(currentSwap.destAmount)
).toString();

newBalancers[exchangeKey].percent += s.percent;
newBalancers[exchangeKey].data.exchangeProxy = s.data.exchangeProxy;
newBalancers[exchangeKey].data.gasUSD = (
parseFloat(newBalancers[exchangeKey].data.gasUSD) +
parseFloat(s.data.gasUSD)
lastExchangeSwap.percent += currentSwap.percent;
lastExchangeSwap.data.gasUSD = (
parseFloat(lastExchangeSwap.data.gasUSD) +
parseFloat(currentSwap.data.gasUSD)
).toFixed(6);

newBalancers[exchangeKey].data.swaps.push({
poolId: s.data.poolId,
amount: side === SwapSide.SELL ? s.srcAmount : s.destAmount,
lastExchangeSwap.data.swaps.push({
poolId: currentSwap.data.poolId,
amount:
side === SwapSide.SELL
? currentSwap.srcAmount
: currentSwap.destAmount,
tokenIn: currentSwap.data.tokenIn,
tokenOut: currentSwap.data.tokenOut,
});
newBalancers[exchangeKey].poolAddresses!.push(s.poolAddresses![0]);
lastExchangeSwap.poolAddresses!.push(currentSwap.poolAddresses![0]);
} else {
optimizedSwap.push(s);
lastExchange = _.cloneDeep(s);
lastExchange.swapExchanges[0].data = {};
lastExchange.swapExchanges[0].data.gasUSD =
s.swapExchanges[0].data.gasUSD;
lastExchange.swapExchanges[0].data.swaps = [
{
poolId: s.swapExchanges[0].data.poolId,
amount:
side === SwapSide.SELL
? s.swapExchanges[0].srcAmount
: s.swapExchanges[0].destAmount,
tokenIn: s.swapExchanges[0].data.tokenIn,
tokenOut: s.swapExchanges[0].data.tokenOut,
},
];
optimizedRate.push(lastExchange);
}
});
optimizedSwap = optimizedSwap.concat(Object.values(newBalancers));
return optimizedSwap;
return optimizedRate;
};

or.bestRoute = or.bestRoute.map(r => ({
...r,
swaps: r.swaps.map(s => ({
...s,
swapExchanges: fixSwap(s.swapExchanges, or.side),
})),
swaps: fixSwap(r.swaps, or.side),
}));
return or;
}
4 changes: 4 additions & 0 deletions src/dex/balancer-v2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export interface SubgraphPoolBase {
export type BalancerSwapV2 = {
poolId: string;
amount: string;
tokenIn: string;
tokenOut: string;
};

export type OptimizedBalancerV2Data = {
Expand Down Expand Up @@ -139,6 +141,8 @@ export type BalancerV2DirectParam = [

export type BalancerV2Data = {
poolId: string;
tokenIn: string;
tokenOut: string;
};

export type DexParams = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@ const customPlain3CoinThree: calc_token_amount = (
return (diff * token_amount) / D0;
};

const factoryPlain2Basic: calc_token_amount = (
self: IPoolContext,
state: PoolState,
amounts: bigint[],
is_deposit: boolean,
) => {
const { N_COINS } = self.constants;
const amp = state.A;
const balances = [...state.balances];
const D0 = self.get_D(self, balances, amp);
for (const i of _.range(N_COINS)) {
if (is_deposit) balances[i] += amounts[i];
else balances[i] -= amounts[i];
}
const D1 = self.get_D(self, balances, amp);

if (state.totalSupply === undefined) {
throw new Error(
`${self.IMPLEMENTATION_NAME} customPlain3CoinThree: totalSupply is not provided`,
);
}

const token_amount = state.totalSupply;
let diff = 0n;
if (is_deposit) {
diff = D1 - D0;
} else {
diff = D0 - D1;
}
return (diff * token_amount) / D0;
};

const customAvalanche3CoinLending: calc_token_amount = (
self: IPoolContext,
state: PoolState,
Expand Down Expand Up @@ -114,7 +146,7 @@ const implementations: Record<ImplementationNames, calc_token_amount> = {
[ImplementationNames.FACTORY_META_USD_BALANCES_FRAX_USDC]: notImplemented,

[ImplementationNames.FACTORY_PLAIN_2_BALANCES]: notImplemented,
[ImplementationNames.FACTORY_PLAIN_2_BASIC]: notImplemented,
[ImplementationNames.FACTORY_PLAIN_2_BASIC]: factoryPlain2Basic,
[ImplementationNames.FACTORY_PLAIN_2_ETH]: notImplemented,
[ImplementationNames.FACTORY_PLAIN_2_OPTIMIZED]: notImplemented,

Expand Down
2 changes: 1 addition & 1 deletion src/dex/uniswap-v3/uniswap-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ export class UniswapV3
e,
);
} else {
// on unkown error mark as failed and increase retryCount for retry init strategy
// on unknown error mark as failed and increase retryCount for retry init strategy
// note: state would be null by default which allows to fallback
this.logger.warn(
`${this.dexKey}: Can not generate pool state for srcAddress=${srcAddress}, destAddress=${destAddress}, fee=${fee} pool fallback to rpc and retry every ${this.config.initRetryFrequency} times, initRetryAttemptCount=${pool.initRetryAttemptCount}`,
Expand Down

0 comments on commit bcda72d

Please sign in to comment.