From e07bb8df4f9fc5adc04cb948bca4c074e10be370 Mon Sep 17 00:00:00 2001 From: padraic Date: Thu, 9 Feb 2023 13:33:24 +0000 Subject: [PATCH 1/2] Restructure code to disable viaIR --- contracts/libraries/HyperdriveMath.sol | 68 +++---- contracts/libraries/YieldSpaceMath.sol | 259 ++++++++++++++++--------- foundry.toml | 2 +- 3 files changed, 205 insertions(+), 124 deletions(-) diff --git a/contracts/libraries/HyperdriveMath.sol b/contracts/libraries/HyperdriveMath.sol index ba509c0cb..89029633a 100644 --- a/contracts/libraries/HyperdriveMath.sol +++ b/contracts/libraries/HyperdriveMath.sol @@ -124,7 +124,7 @@ library HyperdriveMath { uint256 userDelta ) { - uint256 flat = _amountIn.mulDown( + userDelta = _amountIn.mulDown( FixedPointMath.ONE_18.sub(_timeRemaining) ); if (_isBondOut) { @@ -132,25 +132,21 @@ library HyperdriveMath { // purchased to be fully matured and we use the remaining // timeRemaining*amountIn shares to purchase newly minted bonds on a // YieldSpace curve configured to timeRemaining = 1. - uint256 curveIn = _amountIn.mulDown(_timeRemaining); - uint256 curveOut = YieldSpaceMath.calculateOutGivenIn( + poolBondDelta = YieldSpaceMath.calculateOutGivenIn( // Credit the share reserves by the flat trade. - _shareReserves.add(flat), + _shareReserves.add(userDelta), // Debit the bond reserves by the flat trade. - _bondReserves.sub(flat.mulDown(_sharePrice)), + _bondReserves.sub(userDelta.mulDown(_sharePrice)), _bondReserveAdjustment, - curveIn, + _amountIn.mulDown(_timeRemaining), // curveIn FixedPointMath.ONE_18, _timeStretch, _sharePrice, _initialSharePrice, _isBondOut ); - return ( - _amountIn, - curveOut, - flat.mulDown(_sharePrice).add(curveOut) - ); + userDelta = userDelta.mulDown(_sharePrice).add(poolBondDelta); + poolShareDelta = _amountIn; } else { // We consider (1-timeRemaining)*amountIn of the bonds to be fully // matured and timeRemaining*amountIn of the bonds to be newly @@ -158,25 +154,27 @@ library HyperdriveMath { // (our result is given in shares, so we divide the one-to-one // redemption by the share price) and the newly minted bonds are // traded on a YieldSpace curve configured to timeRemaining = 1. - flat = flat.divDown(_sharePrice); - uint256 curveIn = _amountIn.mulDown(_timeRemaining).divDown( + userDelta = userDelta.divDown(_sharePrice); + poolBondDelta = _amountIn.mulDown(_timeRemaining).divDown( _sharePrice ); - uint256 curveOut = YieldSpaceMath.calculateOutGivenIn( - // Debit the share reserves by the flat trade. - _shareReserves.sub(flat), - // Credit the bond reserves by the flat trade. - _bondReserves.add(flat.mulDown(_sharePrice)), - _bondReserveAdjustment, - curveIn, - FixedPointMath.ONE_18, - _timeStretch, - _sharePrice, - _initialSharePrice, - _isBondOut + userDelta = userDelta.add( + // curveOut + YieldSpaceMath.calculateOutGivenIn( + // Debit the share reserves by the flat trade. + _shareReserves.sub(userDelta), + // Credit the bond reserves by the flat trade. + _bondReserves.add(userDelta.mulDown(_sharePrice)), + _bondReserveAdjustment, + poolBondDelta, // curveIn + FixedPointMath.ONE_18, + _timeStretch, + _sharePrice, + _initialSharePrice, + _isBondOut + ) ); - uint256 shareDelta = flat.add(curveOut); - return (shareDelta, curveIn, shareDelta); + poolShareDelta = userDelta; } } @@ -224,26 +222,24 @@ library HyperdriveMath { // the one-to-one redemption by the share price) and the newly // minted bonds are traded on a YieldSpace curve configured to // timeRemaining = 1. - uint256 flat = _amountOut + userDelta = _amountOut .mulDown(FixedPointMath.ONE_18.sub(_timeRemaining)) .divDown(_sharePrice); - uint256 curveOut = _amountOut.mulDown(_timeRemaining).divDown( - _sharePrice - ); - uint256 curveIn = YieldSpaceMath.calculateInGivenOut( + poolBondDelta = _amountOut.mulDown(_timeRemaining).divDown(_sharePrice); + userDelta += YieldSpaceMath.calculateInGivenOut( // Credit the share reserves by the flat trade. - _shareReserves.add(flat), + _shareReserves.add(userDelta), // Debit the bond reserves by the flat trade. - _bondReserves.sub(flat.mulDown(_sharePrice)), + _bondReserves.sub(userDelta.mulDown(_sharePrice)), _bondReserveAdjustment, - curveOut, + poolBondDelta, FixedPointMath.ONE_18, _timeStretch, _sharePrice, _initialSharePrice, false ); - return (flat.add(curveIn), curveOut, flat.add(curveIn)); + poolShareDelta = userDelta; } // TODO: Use an allocation scheme that doesn't punish early LPs. diff --git a/contracts/libraries/YieldSpaceMath.sol b/contracts/libraries/YieldSpaceMath.sol index 27053f428..74b21ba26 100644 --- a/contracts/libraries/YieldSpaceMath.sol +++ b/contracts/libraries/YieldSpaceMath.sol @@ -14,6 +14,17 @@ import "contracts/libraries/FixedPointMath.sol"; library YieldSpaceMath { using FixedPointMath for uint256; + struct YieldSpaceArgs { + uint256 shareReserves; + uint256 bondReserves; + uint256 bondReserveAdjustment; + uint256 amount; + uint256 t; + uint256 s; + uint256 c; + uint256 mu; + } + /// Calculates the amount of bond a user would get for given amount of shares. /// @param shareReserves yield bearing vault shares reserve amount, unit is shares /// @param bondReserves bond reserves amount, unit is the face value in underlying @@ -35,51 +46,21 @@ library YieldSpaceMath { uint256 c, uint256 mu, bool isBondOut - ) internal pure returns (uint256 result) { - uint256 outReserves; - uint256 rhs; - // Notes: 1 >= 1-st >= 0 - uint256 oneMinusT = FixedPointMath.ONE_18.sub(s.mulDown(t)); - // c/mu - uint256 cDivMu = c.divDown(mu); - // Adjust the bond reserve, optionally shifts the curve around the inflection point - uint256 modifiedBondReserves = bondReserves.add(bondReserveAdjustment); - // c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - uint256 k = cDivMu - .mulDown(mu.mulDown(shareReserves).pow(oneMinusT)) - .add(modifiedBondReserves.pow(oneMinusT)); - - if (isBondOut) { - // bondOut = bondReserves - ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves + shareIn))^(1-t) )^(1 / (1 - t)) - outReserves = modifiedBondReserves; - // (mu*(shareReserves + amountIn))^(1-t) - uint256 newScaledShareReserves = mu - .mulDown(shareReserves.add(amountIn)) - .pow(oneMinusT); - // c/mu * (mu*(shareReserves + amountIn))^(1-t) - newScaledShareReserves = cDivMu.mulDown(newScaledShareReserves); - // Notes: k - newScaledShareReserves >= 0 to avoid a complex number - // ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves + amountIn))^(1-t) )^(1 / (1 - t)) - rhs = k.sub(newScaledShareReserves).pow( - FixedPointMath.ONE_18.divDown(oneMinusT) - ); - } else { - // shareOut = shareReserves - [ ( c/mu * (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves + bondIn)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu - outReserves = shareReserves; - // (bondReserves + bondIn)^(1-t) - uint256 newScaledBondReserves = modifiedBondReserves - .add(amountIn) - .pow(oneMinusT); - // Notes: k - newScaledBondReserves >= 0 to avoid a complex number - // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves + bondIn)^(1-t) ) / c/u ]^(1 / (1 - t)) - rhs = k.sub(newScaledBondReserves).divDown(cDivMu).pow( - FixedPointMath.ONE_18.divDown(oneMinusT) - ); - // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves + bondIn)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu - rhs = rhs.divDown(mu); - } - // Notes: outReserves - rhs >= 0, but i think avoiding a complex number in the step above ensures this never happens - result = outReserves.sub(rhs); + ) internal pure returns (uint256) { + YieldSpaceArgs memory args = YieldSpaceArgs({ + shareReserves: shareReserves, + bondReserves: bondReserves, + bondReserveAdjustment: bondReserveAdjustment, + amount: amountIn, + t: t, + s: s, + c: c, + mu: mu + }); + return + isBondOut + ? _calculateOutGivenInBondOut(args) + : _calculateOutGivenInBondIn(args); } /// @dev Calculates the amount of an asset that will be received given a @@ -104,52 +85,156 @@ library YieldSpaceMath { uint256 c, uint256 mu, bool isBondIn - ) internal pure returns (uint256 result) { - uint256 inReserves; - uint256 rhs; + ) internal pure returns (uint256) { + YieldSpaceArgs memory args = YieldSpaceArgs({ + shareReserves: shareReserves, + bondReserves: bondReserves, + bondReserveAdjustment: bondReserveAdjustment, + amount: amountOut, + t: t, + s: s, + c: c, + mu: mu + }); + return + isBondIn + ? _calculateInGivenOutBondIn(args) + : _calculateInGivenOutBondOut(args); + } + + function _calculateOutGivenInBondOut( + YieldSpaceArgs memory _args + ) internal pure returns (uint256) { + ( + uint256 oneMinusT, + uint256 cDivMu, + uint256 modifiedBondReserves, + uint256 k + ) = _internalYieldSpaceCalculations(_args); + + // bondOut = bondReserves - ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves + shareIn))^(1-t) )^(1 / (1 - t)) + uint256 outReserves = modifiedBondReserves; + + // (mu*(shareReserves + amountIn))^(1-t) + uint256 newScaledShareReserves = _args + .mu + .mulDown(_args.shareReserves.add(_args.amount)) + .pow(oneMinusT); + // c/mu * (mu*(shareReserves + amountIn))^(1-t) + newScaledShareReserves = cDivMu.mulDown(newScaledShareReserves); + // Notes: k - newScaledShareReserves >= 0 to avoid a complex number + // ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves + amountIn))^(1-t) )^(1 / (1 - t)) + uint256 rhs = k.sub(newScaledShareReserves).pow( + FixedPointMath.ONE_18.divDown(oneMinusT) + ); + + return outReserves.sub(rhs); + } + + function _calculateOutGivenInBondIn( + YieldSpaceArgs memory _args + ) internal pure returns (uint256) { + ( + uint256 oneMinusT, + uint256 cDivMu, + uint256 modifiedBondReserves, + uint256 k + ) = _internalYieldSpaceCalculations(_args); + + uint256 outReserves = _args.shareReserves; + // (bondReserves + bondIn)^(1-t) + uint256 newScaledBondReserves = modifiedBondReserves + .add(_args.amount) + .pow(oneMinusT); + // Notes: k - newScaledBondReserves >= 0 to avoid a complex number + // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves + bondIn)^(1-t) ) / c/u ]^(1 / (1 - t)) + uint256 rhs = k.sub(newScaledBondReserves).divDown(cDivMu).pow( + FixedPointMath.ONE_18.divDown(oneMinusT) + ); + // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves + bondIn)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu + rhs = rhs.divDown(_args.mu); + + return outReserves.sub(rhs); + } + + function _calculateInGivenOutBondIn( + YieldSpaceArgs memory _args + ) internal pure returns (uint256) { + ( + uint256 oneMinusT, + uint256 cDivMu, + uint256 modifiedBondReserves, + uint256 k + ) = _internalYieldSpaceCalculations(_args); + + // bondIn = ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves - shareOut))^(1-t) )^(1 / (1 - t)) - bond_reserves + uint256 inReserves = modifiedBondReserves; + // (mu*(shareReserves - amountOut))^(1-t) + uint256 newScaledShareReserves = _args + .mu + .mulDown(_args.shareReserves.sub(_args.amount)) + .pow(oneMinusT); + // c/mu * (mu*(shareReserves - amountOut))^(1-t) + newScaledShareReserves = cDivMu.mulDown(newScaledShareReserves); + // Notes: k - newScaledShareReserves >= 0 to avoid a complex number + // ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves - amountOut))^(1-t) )^(1 / (1 - t)) + uint256 rhs = k.sub(newScaledShareReserves).pow( + FixedPointMath.ONE_18.divDown(oneMinusT) + ); + + return rhs.sub(inReserves); + } + + function _calculateInGivenOutBondOut( + YieldSpaceArgs memory _args + ) internal pure returns (uint256) { + ( + uint256 oneMinusT, + uint256 cDivMu, + uint256 modifiedBondReserves, + uint256 k + ) = _internalYieldSpaceCalculations(_args); + + // shareOut = [ ( c/mu * (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - bondOut)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu - share_reserves + uint256 inReserves = _args.shareReserves; + // (bondReserves - amountOut)^(1-t) + uint256 newScaledBondReserves = modifiedBondReserves + .sub(_args.amount) + .pow(oneMinusT); + // Notes: k - newScaledBondReserves >= 0 to avoid a complex number + // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - amountOut)^(1-t) ) / c/u ]^(1 / (1 - t)) + uint256 rhs = k.sub(newScaledBondReserves).divDown(cDivMu).pow( + FixedPointMath.ONE_18.divDown(oneMinusT) + ); + // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - amountOut)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu + rhs = rhs.divDown(_args.mu); + + return rhs.sub(inReserves); + } + + function _internalYieldSpaceCalculations( + YieldSpaceArgs memory _args + ) + internal + pure + returns ( + uint256 oneMinusT, + uint256 cDivMu, + uint256 modifiedBondReserves, + uint256 k + ) + { // Notes: 1 >= 1-st >= 0 - uint256 oneMinusT = FixedPointMath.ONE_18.sub(s.mulDown(t)); + oneMinusT = FixedPointMath.ONE_18.sub(_args.s.mulDown(_args.t)); // c/mu - uint256 cDivMu = c.divDown(mu); + cDivMu = _args.c.divDown(_args.mu); // Adjust the bond reserve, optionally shifts the curve around the inflection point - uint256 modifiedBondReserves = bondReserves.add(bondReserveAdjustment); + modifiedBondReserves = _args.bondReserves.add( + _args.bondReserveAdjustment + ); // c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - uint256 k = cDivMu - .mulDown(mu.mulDown(shareReserves).pow(oneMinusT)) + k = cDivMu + .mulDown(_args.mu.mulDown(_args.shareReserves).pow(oneMinusT)) .add(modifiedBondReserves.pow(oneMinusT)); - - if (isBondIn) { - // bondIn = ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves - shareOut))^(1-t) )^(1 / (1 - t)) - bond_reserves - inReserves = modifiedBondReserves; - // (mu*(shareReserves - amountOut))^(1-t) - uint256 newScaledShareReserves = mu - .mulDown(shareReserves.sub(amountOut)) - .pow(oneMinusT); - // c/mu * (mu*(shareReserves - amountOut))^(1-t) - newScaledShareReserves = cDivMu.mulDown(newScaledShareReserves); - // Notes: k - newScaledShareReserves >= 0 to avoid a complex number - // ( c/mu * (mu*shareReserves)^(1-t) + bondReserves^(1-t) - c/mu * (mu*(shareReserves - amountOut))^(1-t) )^(1 / (1 - t)) - rhs = k.sub(newScaledShareReserves).pow( - FixedPointMath.ONE_18.divDown(oneMinusT) - ); - } else { - // shareOut = [ ( c/mu * (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - bondOut)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu - share_reserves - inReserves = shareReserves; - // (bondReserves - amountOut)^(1-t) - uint256 newScaledBondReserves = modifiedBondReserves - .sub(amountOut) - .pow(oneMinusT); - // Notes: k - newScaledBondReserves >= 0 to avoid a complex number - // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - amountOut)^(1-t) ) / c/u ]^(1 / (1 - t)) - rhs = k.sub(newScaledBondReserves).divDown(cDivMu).pow( - FixedPointMath.ONE_18.divDown(oneMinusT) - ); - // [( (mu * shareReserves)^(1-t) + bondReserves^(1-t) - (bondReserves - amountOut)^(1-t) ) / c/u ]^(1 / (1 - t)) / mu - rhs = rhs.divDown(mu); - } - // TODO: Double check this. - // - // Notes: rhs - inReserves >= 0, but i think avoiding a complex number in the step above ensures this never happens - result = rhs.sub(inReserves); } } diff --git a/foundry.toml b/foundry.toml index df0b7e6b4..16fcc9adc 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,7 +16,7 @@ optimizer = true # The number of optimizer runs optimizer_runs = 7500 # Whether or not to use the Yul intermediate representation compilation pipeline -via_ir = true +via_ir = false remappings = ['forge-std=lib/forge-std/src', '@openzeppelin/=node_modules/@openzeppelin'] # gas limit - max u64 From 81d416671358f2c48edef5eccd6fadf7faf23682 Mon Sep 17 00:00:00 2001 From: padraic Date: Thu, 9 Feb 2023 13:37:01 +0000 Subject: [PATCH 2/2] fix linting --- contracts/libraries/YieldSpaceMath.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/libraries/YieldSpaceMath.sol b/contracts/libraries/YieldSpaceMath.sol index 74b21ba26..7bf9989eb 100644 --- a/contracts/libraries/YieldSpaceMath.sol +++ b/contracts/libraries/YieldSpaceMath.sol @@ -104,7 +104,7 @@ library YieldSpaceMath { function _calculateOutGivenInBondOut( YieldSpaceArgs memory _args - ) internal pure returns (uint256) { + ) private pure returns (uint256) { ( uint256 oneMinusT, uint256 cDivMu, @@ -133,7 +133,7 @@ library YieldSpaceMath { function _calculateOutGivenInBondIn( YieldSpaceArgs memory _args - ) internal pure returns (uint256) { + ) private pure returns (uint256) { ( uint256 oneMinusT, uint256 cDivMu, @@ -159,7 +159,7 @@ library YieldSpaceMath { function _calculateInGivenOutBondIn( YieldSpaceArgs memory _args - ) internal pure returns (uint256) { + ) private pure returns (uint256) { ( uint256 oneMinusT, uint256 cDivMu, @@ -187,7 +187,7 @@ library YieldSpaceMath { function _calculateInGivenOutBondOut( YieldSpaceArgs memory _args - ) internal pure returns (uint256) { + ) private pure returns (uint256) { ( uint256 oneMinusT, uint256 cDivMu, @@ -215,7 +215,7 @@ library YieldSpaceMath { function _internalYieldSpaceCalculations( YieldSpaceArgs memory _args ) - internal + private pure returns ( uint256 oneMinusT,