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..7bf9989eb 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 + ) private 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 + ) private 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 + ) private 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 + ) private 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 + ) + private + 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