Skip to content

Commit

Permalink
Linear pool fix rate math (balancer#861)
Browse files Browse the repository at this point in the history
* fix wrong use of rate in linear math

* simplify _calcWrappedOutPerMainIn and _calcWrappedInPerMainOut math functions

* fix rounding in _calcWrappedInPerMainOut

* update typecript math with fix

* simplify typecript math
  • Loading branch information
dmf7z authored Sep 23, 2021
1 parent ce5fcc7 commit 7f6afba
Show file tree
Hide file tree
Showing 6 changed files with 13 additions and 38 deletions.
8 changes: 2 additions & 6 deletions pkg/pool-linear/contracts/LinearMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,27 @@ contract LinearMath {
function _calcWrappedOutPerMainIn(
uint256 mainIn,
uint256 mainBalance,
uint256 wrappedBalance,
Params memory params
) internal pure returns (uint256) {
// Amount out, so we round down overall.

uint256 previousNominalMain = _toNominal(mainBalance, params);
uint256 afterNominalMain = _toNominal(mainBalance.add(mainIn), params);
uint256 deltaNominalMain = afterNominalMain.sub(previousNominalMain);
uint256 newWrappedBalance = wrappedBalance.sub(deltaNominalMain.mulDown(params.rate));
return wrappedBalance.sub(newWrappedBalance);
return deltaNominalMain.divDown(params.rate);
}

function _calcWrappedInPerMainOut(
uint256 mainOut,
uint256 mainBalance,
uint256 wrappedBalance,
Params memory params
) internal pure returns (uint256) {
// Amount in, so we round up overall.

uint256 previousNominalMain = _toNominal(mainBalance, params);
uint256 afterNominalMain = _toNominal(mainBalance.sub(mainOut), params);
uint256 deltaNominalMain = previousNominalMain.sub(afterNominalMain);
uint256 newWrappedBalance = wrappedBalance.add(deltaNominalMain.mulUp(params.rate));
return newWrappedBalance.sub(wrappedBalance);
return deltaNominalMain.divUp(params.rate);
}

function _calcMainInPerBptOut(
Expand Down
4 changes: 2 additions & 2 deletions pkg/pool-linear/contracts/LinearPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ contract LinearPool is BasePool, IGeneralPool, LinearMath, IRateProvider {
_require(request.tokenOut == _wrappedToken || request.tokenOut == IERC20(this), Errors.INVALID_TOKEN);
return
request.tokenOut == _wrappedToken
? _calcWrappedOutPerMainIn(request.amount, balances[_mainIndex], balances[_wrappedIndex], params)
? _calcWrappedOutPerMainIn(request.amount, balances[_mainIndex], params)
: _calcBptOutPerMainIn(
request.amount,
balances[_mainIndex],
Expand Down Expand Up @@ -310,7 +310,7 @@ contract LinearPool is BasePool, IGeneralPool, LinearMath, IRateProvider {
_require(request.tokenIn == _wrappedToken || request.tokenIn == IERC20(this), Errors.INVALID_TOKEN);
return
request.tokenIn == _wrappedToken
? _calcWrappedInPerMainOut(request.amount, balances[_mainIndex], balances[_wrappedIndex], params)
? _calcWrappedInPerMainOut(request.amount, balances[_mainIndex], params)
: _calcBptInPerMainOut(
request.amount,
balances[_mainIndex],
Expand Down
6 changes: 2 additions & 4 deletions pkg/pool-linear/contracts/test/MockLinearMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,17 @@ contract MockLinearMath is LinearMath {
function calcWrappedOutPerMainIn(
uint256 mainIn,
uint256 mainBalance,
uint256 wrappedBalance,
Params memory params
) external pure returns (uint256) {
return _calcWrappedOutPerMainIn(mainIn, mainBalance, wrappedBalance, params);
return _calcWrappedOutPerMainIn(mainIn, mainBalance, params);
}

function calcWrappedInPerMainOut(
uint256 mainOut,
uint256 mainBalance,
uint256 wrappedBalance,
Params memory params
) external pure returns (uint256) {
return _calcWrappedInPerMainOut(mainOut, mainBalance, wrappedBalance, params);
return _calcWrappedInPerMainOut(mainOut, mainBalance, params);
}

function calcMainInPerBptOut(
Expand Down
4 changes: 2 additions & 2 deletions pkg/pool-linear/test/LinearMath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe('LinearMath', function () {
const mainBalance = fp(51);
const wrappedBalance = fp(0);

const wrappedIn = await math.calcWrappedInPerMainOut(mainOut, mainBalance, wrappedBalance, params);
const wrappedIn = await math.calcWrappedInPerMainOut(mainOut, mainBalance, params);
expect(wrappedBalance.add(wrappedIn)).to.be.equalWithError(fp(10.10101010101010101), EXPECTED_RELATIVE_ERROR);
});

Expand All @@ -107,7 +107,7 @@ describe('LinearMath', function () {
const mainBalance = fp(41);
const wrappedBalance = fp(10.10101010101010101);

const wrappedOut = await math.calcWrappedOutPerMainIn(mainIn, mainBalance, wrappedBalance, params);
const wrappedOut = await math.calcWrappedOutPerMainIn(mainIn, mainBalance, params);
expect(wrappedBalance.sub(wrappedOut)).to.be.equalWithError(fp(5.050505050505050505), EXPECTED_RELATIVE_ERROR);
});

Expand Down
7 changes: 1 addition & 6 deletions pkg/pool-linear/test/LinearPool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,7 @@ describe('LinearPool', function () {
balances: currentBalances,
});

const expected = math.calcWrappedInPerMainOut(
amount,
currentBalances[pool.mainIndex],
currentBalances[pool.wrappedIndex],
params
);
const expected = math.calcWrappedInPerMainOut(amount, currentBalances[pool.mainIndex], params);

expect(result).to.be.equalWithError(bn(expected), EXPECTED_RELATIVE_ERROR);

Expand Down
22 changes: 4 additions & 18 deletions pkg/pool-linear/test/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,41 +54,27 @@ export function calcBptInPerMainOut(
return toFp(bptIn);
}

export function calcWrappedOutPerMainIn(
fpMainIn: BigNumber,
fpMainBalance: BigNumber,
fpWrappedBalance: BigNumber,
params: Params
): Decimal {
export function calcWrappedOutPerMainIn(fpMainIn: BigNumber, fpMainBalance: BigNumber, params: Params): Decimal {
const mainIn = fromFp(fpMainIn);
const mainBalance = fromFp(fpMainBalance);
const wrappedBalance = fromFp(fpWrappedBalance);
const rate = fromFp(params.rate);

const previousNominalMain = toNominal(mainBalance, params);
const afterNominalMain = toNominal(mainBalance.add(mainIn), params);
const deltaNominalMain = afterNominalMain.sub(previousNominalMain);
const newWrappedBalance = wrappedBalance.sub(deltaNominalMain.mul(rate));
const wrappedOut = wrappedBalance.sub(newWrappedBalance);
const wrappedOut = deltaNominalMain.div(rate);
return toFp(wrappedOut);
}

export function calcWrappedInPerMainOut(
fpMainOut: BigNumber,
fpMainBalance: BigNumber,
fpWrappedBalance: BigNumber,
params: Params
): Decimal {
export function calcWrappedInPerMainOut(fpMainOut: BigNumber, fpMainBalance: BigNumber, params: Params): Decimal {
const mainOut = fromFp(fpMainOut);
const mainBalance = fromFp(fpMainBalance);
const wrappedBalance = fromFp(fpWrappedBalance);
const rate = fromFp(params.rate);

const previousNominalMain = toNominal(mainBalance, params);
const afterNominalMain = toNominal(mainBalance.sub(mainOut), params);
const deltaNominalMain = previousNominalMain.sub(afterNominalMain);
const newWrappedBalance = wrappedBalance.add(deltaNominalMain.mul(rate));
const wrappedIn = newWrappedBalance.sub(wrappedBalance);
const wrappedIn = deltaNominalMain.div(rate);
return toFp(wrappedIn);
}

Expand Down

0 comments on commit 7f6afba

Please sign in to comment.