Skip to content

Commit

Permalink
Merge pull request #139 from BeanstalkFarms/s2-newton-revert
Browse files Browse the repository at this point in the history
Add explicit reverts in functions that use Newton's method after non-convergence [#19]
  • Loading branch information
nickkatsios authored Aug 21, 2024
2 parents e824771 + 453d388 commit e60e660
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 43 deletions.
27 changes: 9 additions & 18 deletions src/Well.sol
Original file line number Diff line number Diff line change
Expand Up @@ -484,12 +484,9 @@ contract Well is ERC20PermitUpgradeable, IWell, IWellErrors, ReentrancyGuardUpgr
/**
* @dev Assumes that no tokens involved incur a fee on transfer.
*/
function getAddLiquidityOut(uint256[] memory tokenAmountsIn)
external
view
readOnlyNonReentrant
returns (uint256 lpAmountOut)
{
function getAddLiquidityOut(
uint256[] memory tokenAmountsIn
) external view readOnlyNonReentrant returns (uint256 lpAmountOut) {
IERC20[] memory _tokens = tokens();
uint256 tokensLength = _tokens.length;
uint256[] memory reserves = _getReserves(tokensLength);
Expand Down Expand Up @@ -527,12 +524,9 @@ contract Well is ERC20PermitUpgradeable, IWell, IWellErrors, ReentrancyGuardUpgr
emit RemoveLiquidity(lpAmountIn, tokenAmountsOut, recipient);
}

function getRemoveLiquidityOut(uint256 lpAmountIn)
external
view
readOnlyNonReentrant
returns (uint256[] memory tokenAmountsOut)
{
function getRemoveLiquidityOut(
uint256 lpAmountIn
) external view readOnlyNonReentrant returns (uint256[] memory tokenAmountsOut) {
IERC20[] memory _tokens = tokens();
uint256[] memory reserves = _getReserves(_tokens.length);
uint256 lpTokenSupply = totalSupply();
Expand Down Expand Up @@ -620,12 +614,9 @@ contract Well is ERC20PermitUpgradeable, IWell, IWellErrors, ReentrancyGuardUpgr
emit RemoveLiquidity(lpAmountIn, tokenAmountsOut, recipient);
}

function getRemoveLiquidityImbalancedIn(uint256[] calldata tokenAmountsOut)
external
view
readOnlyNonReentrant
returns (uint256 lpAmountIn)
{
function getRemoveLiquidityImbalancedIn(
uint256[] calldata tokenAmountsOut
) external view readOnlyNonReentrant returns (uint256 lpAmountIn) {
IERC20[] memory _tokens = tokens();
uint256 tokensLength = _tokens.length;
uint256[] memory reserves = _getReserves(tokensLength);
Expand Down
5 changes: 4 additions & 1 deletion src/functions/Stable2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
if (prevReserves - lpTokenSupply <= 1) return lpTokenSupply;
}
}
revert("Non convergence: calcLpTokenSupply");
}

/**
Expand Down Expand Up @@ -140,7 +141,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
}
}
}
revert("did not find convergence");
revert("Non convergence: calcReserve");
}

/**
Expand Down Expand Up @@ -236,6 +237,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
}
}
}
revert("Non convergence: calcReserveAtRatioSwap");
}

/**
Expand Down Expand Up @@ -301,6 +303,7 @@ contract Stable2 is ProportionalLPToken2, IBeanstalkWellFunction {
}
}
}
revert("Non convergence: calcReserveAtRatioLiquidity");
}

/**
Expand Down
7 changes: 3 additions & 4 deletions src/interfaces/IWell.sol
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,9 @@ interface IWell {
* @param tokenAmountsOut The amount of each underlying token to receive; MUST match the indexing of {Well.tokens}
* @return lpAmountIn The amount of LP tokens burned
*/
function getRemoveLiquidityImbalancedIn(uint256[] calldata tokenAmountsOut)
external
view
returns (uint256 lpAmountIn);
function getRemoveLiquidityImbalancedIn(
uint256[] calldata tokenAmountsOut
) external view returns (uint256 lpAmountIn);

//////////////////// RESERVES ////////////////////

Expand Down
8 changes: 3 additions & 5 deletions src/libraries/LibLastReserveBytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,9 @@ library LibLastReserveBytes {
/**
* @dev Read `n` packed bytes16 reserves at storage position `slot`.
*/
function readLastReserves(bytes32 slot)
internal
view
returns (uint8 n, uint40 lastTimestamp, uint256[] memory lastReserves)
{
function readLastReserves(
bytes32 slot
) internal view returns (uint8 n, uint40 lastTimestamp, uint256[] memory lastReserves) {
// Shortcut: two reserves can be quickly unpacked from one slot
bytes32 temp;
assembly {
Expand Down
14 changes: 6 additions & 8 deletions test/LiquidityHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ contract LiquidityHelper is TestHelper {
return beforeAddLiquidity(action);
}

function beforeAddLiquidity(AddLiquidityAction memory action)
internal
returns (Snapshot memory, AddLiquidityAction memory)
{
function beforeAddLiquidity(
AddLiquidityAction memory action
) internal returns (Snapshot memory, AddLiquidityAction memory) {
Snapshot memory beforeSnapshot = _newSnapshot();

uint256[] memory amountToTransfer = new uint256[](tokens.length);
Expand Down Expand Up @@ -97,10 +96,9 @@ contract LiquidityHelper is TestHelper {
return beforeRemoveLiquidity(action);
}

function beforeRemoveLiquidity(RemoveLiquidityAction memory action)
internal
returns (Snapshot memory, RemoveLiquidityAction memory)
{
function beforeRemoveLiquidity(
RemoveLiquidityAction memory action
) internal returns (Snapshot memory, RemoveLiquidityAction memory) {
Snapshot memory beforeSnapshot = _newSnapshot();

vm.expectEmit(true, true, true, true);
Expand Down
4 changes: 3 additions & 1 deletion test/Stable2/Well.Stable2.AddLiquidity.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ contract WellStable2AddLiquidityTest is LiquidityHelper {
// amounts to add as liquidity
uint256[] memory amounts = new uint256[](2);
amounts[0] = bound(x, 0, type(uint104).max);
amounts[1] = bound(y, 0, type(uint104).max);
// reserve 1 must be at least 1/600th of the value of amounts[0].
uint256 reserve1MinValue = (amounts[0] / 6e2) < 10e18 ? 10e18 : amounts[0] / 6e2;
amounts[1] = bound(y, reserve1MinValue, type(uint104).max);
mintTokens(user, amounts);

Snapshot memory before;
Expand Down
8 changes: 7 additions & 1 deletion test/beanstalk/BeanstalkStable2.calcReserveAtRatioSwap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ contract BeanstalkStable2SwapTest is TestHelper {
for (uint256 i; i < 2; ++i) {
// Upper bound is limited by stableSwap,
// due to the stableswap reserves being extremely far apart.
reserves[i] = bound(reserves[i], 1e18, 1e31);

if (i == 1) {
uint256 reserve1MinValue = (reserves[0] / 6e2) < 10e18 ? 10e18 : reserves[0] / 6e2;
reserves[1] = bound(reserves[i], reserve1MinValue, 1e31);
} else {
reserves[i] = bound(reserves[i], 1e18, 1e31);
}
ratios[i] = bound(ratios[i], 1e18, 4e18);
}

Expand Down
11 changes: 7 additions & 4 deletions test/functions/Stable2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ contract Stable2Test is WellFunctionHelper {
_data = abi.encode(18, 18);
uint256[] memory reserves = new uint256[](2);
reserves[0] = bound(_reserves[0], 10e18, MAX_RESERVE);
reserves[1] = bound(_reserves[1], 10e18, MAX_RESERVE);
// reserve 1 must be at least 1/600th of the value of reserves[0].
uint256 reserve1MinValue = (reserves[0] / 6e2) < 10e18 ? 10e18 : reserves[0] / 6e2;
reserves[1] = bound(_reserves[1], reserve1MinValue, MAX_RESERVE);

uint256 lpTokenSupply = _function.calcLpTokenSupply(reserves, _data);
uint256[] memory underlying = _function.calcLPTokenUnderlying(lpTokenSupply, reserves, lpTokenSupply, _data);
Expand All @@ -137,11 +139,12 @@ contract Stable2Test is WellFunctionHelper {

//////////// FUZZ ////////////

function testFuzz_stableSwap(uint256 x, uint256 y, uint256 a) public {
function testFuzz_stableSwap(uint256 x, uint256 y) public {
uint256[] memory reserves = new uint256[](2);
reserves[0] = bound(x, 10e18, MAX_RESERVE);
reserves[1] = bound(y, 10e18, MAX_RESERVE);
a = bound(a, 1, 1_000_000);
// reserve 1 must be at least 1/600th of the value of reserves[0].
uint256 reserve1MinValue = (reserves[0] / 6e2) < 10e18 ? 10e18 : reserves[0] / 6e2;
reserves[1] = bound(y, reserve1MinValue, MAX_RESERVE);

_data = abi.encode(18, 18);

Expand Down
2 changes: 1 addition & 1 deletion test/integration/interfaces/ICurve.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity 0.8.20;
pragma solidity ^0.8.20;

interface ICurvePool {
function A_precise() external view returns (uint256);
Expand Down

0 comments on commit e60e660

Please sign in to comment.