Handle max in/out ratio reverts in Balancer sampling functions
This commit is contained in:
parent
5f570b772d
commit
a57cf68ee4
@ -125,6 +125,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Pass back fillData from quote reporter",
|
"note": "Pass back fillData from quote reporter",
|
||||||
"pr": 2702
|
"pr": 2702
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix Balancer sampling",
|
||||||
|
"pr": 2711
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,12 @@ contract BalancerSampler {
|
|||||||
/// @dev Base gas limit for Balancer calls.
|
/// @dev Base gas limit for Balancer calls.
|
||||||
uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k
|
uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k
|
||||||
|
|
||||||
|
// Balancer math constants
|
||||||
|
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
|
||||||
|
uint256 constant private BONE = 10 ** 18;
|
||||||
|
uint256 constant private MAX_IN_RATIO = BONE / 2;
|
||||||
|
uint256 constant private MAX_OUT_RATIO = (BONE / 3) + 1 wei;
|
||||||
|
|
||||||
struct BalancerState {
|
struct BalancerState {
|
||||||
uint256 takerTokenBalance;
|
uint256 takerTokenBalance;
|
||||||
uint256 makerTokenBalance;
|
uint256 makerTokenBalance;
|
||||||
@ -67,6 +73,11 @@ contract BalancerSampler {
|
|||||||
poolState.swapFee = pool.getSwapFee();
|
poolState.swapFee = pool.getSwapFee();
|
||||||
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
// Handles this revert scenario:
|
||||||
|
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443
|
||||||
|
if (takerTokenAmounts[i] > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
poolAddress.staticcall.gas(BALANCER_CALL_GAS)(
|
poolAddress.staticcall.gas(BALANCER_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
@ -120,6 +131,11 @@ contract BalancerSampler {
|
|||||||
poolState.swapFee = pool.getSwapFee();
|
poolState.swapFee = pool.getSwapFee();
|
||||||
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
// Handles this revert scenario:
|
||||||
|
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L505
|
||||||
|
if (makerTokenAmounts[i] > _bmul(poolState.makerTokenBalance, MAX_OUT_RATIO)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
poolAddress.staticcall.gas(BALANCER_CALL_GAS)(
|
poolAddress.staticcall.gas(BALANCER_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
@ -140,4 +156,27 @@ contract BalancerSampler {
|
|||||||
takerTokenAmounts[i] = sellAmount;
|
takerTokenAmounts[i] = sellAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Hacked version of Balancer's `bmul` function, returning 0 instead
|
||||||
|
/// of reverting.
|
||||||
|
/// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L63-L73
|
||||||
|
/// @param a The first operand.
|
||||||
|
/// @param b The second operand.
|
||||||
|
/// @param c The result of the multiplication, or 0 if `bmul` would've reverted.
|
||||||
|
function _bmul(uint256 a, uint256 b)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (uint256 c)
|
||||||
|
{
|
||||||
|
uint c0 = a * b;
|
||||||
|
if (a != 0 && c0 / a != b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint c1 = c0 + (BONE / 2);
|
||||||
|
if (c1 < c0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint c2 = c1 / BONE;
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import { bmath, getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor';
|
import { bmath, getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor';
|
||||||
import { Decimal } from 'decimal.js';
|
import { Decimal } from 'decimal.js';
|
||||||
|
|
||||||
|
import { ZERO_AMOUNT } from './constants';
|
||||||
|
|
||||||
// tslint:disable:boolean-naming
|
// tslint:disable:boolean-naming
|
||||||
|
|
||||||
export interface BalancerPool {
|
export interface BalancerPool {
|
||||||
@ -118,6 +120,9 @@ export class BalancerPoolsCache {
|
|||||||
|
|
||||||
// tslint:disable completed-docs
|
// tslint:disable completed-docs
|
||||||
export function computeBalancerSellQuote(pool: BalancerPool, takerFillAmount: BigNumber): BigNumber {
|
export function computeBalancerSellQuote(pool: BalancerPool, takerFillAmount: BigNumber): BigNumber {
|
||||||
|
if (takerFillAmount.isGreaterThan(bmath.bmul(pool.balanceIn, bmath.MAX_IN_RATIO))) {
|
||||||
|
return ZERO_AMOUNT;
|
||||||
|
}
|
||||||
const weightRatio = pool.weightIn.dividedBy(pool.weightOut);
|
const weightRatio = pool.weightIn.dividedBy(pool.weightOut);
|
||||||
const adjustedIn = bmath.BONE.minus(pool.swapFee)
|
const adjustedIn = bmath.BONE.minus(pool.swapFee)
|
||||||
.dividedBy(bmath.BONE)
|
.dividedBy(bmath.BONE)
|
||||||
@ -130,8 +135,8 @@ export function computeBalancerSellQuote(pool: BalancerPool, takerFillAmount: Bi
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function computeBalancerBuyQuote(pool: BalancerPool, makerFillAmount: BigNumber): BigNumber {
|
export function computeBalancerBuyQuote(pool: BalancerPool, makerFillAmount: BigNumber): BigNumber {
|
||||||
if (makerFillAmount.isGreaterThanOrEqualTo(pool.balanceOut)) {
|
if (makerFillAmount.isGreaterThan(bmath.bmul(pool.balanceOut, bmath.MAX_OUT_RATIO))) {
|
||||||
return new BigNumber(0);
|
return ZERO_AMOUNT;
|
||||||
}
|
}
|
||||||
const weightRatio = pool.weightOut.dividedBy(pool.weightIn);
|
const weightRatio = pool.weightOut.dividedBy(pool.weightIn);
|
||||||
const diff = pool.balanceOut.minus(makerFillAmount);
|
const diff = pool.balanceOut.minus(makerFillAmount);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user