Skip to content

Commit

Permalink
Merge branch 'main' into feat/clean
Browse files Browse the repository at this point in the history
  • Loading branch information
clemlak authored Jun 13, 2024
2 parents 995ed4e + 501dd61 commit ea2534f
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 107 deletions.
5 changes: 3 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
src = "src"
out = "out"
libs = ["lib"]
optimizer_runs = 1
optimizer = true

[invariant]
fail_on_revert = false
Expand All @@ -10,6 +12,5 @@ fail_on_revert = false
mainnet = "${MAINNET_RPC_URL}"
op_sepolia = "${OP_SEPOLIA_RPC_URL}"
testnet = "${TESTNET_RPC_URL}"
rmm_testnet = "${RMM_TESTNET_RPC_URL}"

unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 1, url = "${RMM_TESTNET_RPC_URL}" }
unknown_chain = { key = "${TENDERLY_ACCESS_KEY}", chain = 753712, url = "${TESTNET_RPC_URL}" }
126 changes: 60 additions & 66 deletions src/RMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,45 @@ contract RMM is ERC20 {
);
}

function swapExactTokenForYt(address token, uint256 amountIn, uint256 minSyMinted, uint256 minYtOut, address to)
/// @dev Swaps SY for YT, sending at least `minAmountOut` YT to `to`.
/// @notice `amountIn` is an amount of PT that needs to be minted from the SY in and the SY flash swapped from the pool
function swapExactSyForYt(uint256 amountIn, uint256 minAmountOut, address to)
public
lock
returns (uint256 amountOut, int256 deltaLiquidity)
{
PYIndex index = YT.newIndex();
uint256 amountInWad;
uint256 amountOutWad;
uint256 strike_;

(amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) =
prepareSwapPtIn(amountIn, block.timestamp, index);

_adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index);

// SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender
uint256 delta = index.assetToSyUp(amountInWad) - amountOutWad;
uint256 ytOut = amountOut + delta;
(uint256 debitNative) = _debit(address(SY), delta);


amountOut = mintPtYt(ytOut, address(this));

if (amountOut < minAmountOut) {
revert InsufficientOutput(amountInWad, minAmountOut, amountOut);
}

_credit(address(YT), to, amountOut);

emit Swap(msg.sender, to, address(SY), address(YT), debitNative, amountOut, deltaLiquidity);
}

function swapExactTokenForYt(address token, uint256 amountTokenIn, uint256 amountPtForFlashSwap, uint256 minSyMinted, uint256 minYtOut, address to)
external
payable
lock
returns (uint256 amountYtOut, int256 deltaLiquidity)
returns (uint256 amountOut, int256 deltaLiquidity)
{
// initialize to msg.value
uint256 debitNative = msg.value;
Expand All @@ -125,47 +159,51 @@ contract RMM is ERC20 {
}

if (msg.value > 0 && SY.isValidTokenIn(address(0))) {
amountSyMinted += SY.deposit{value: msg.value}(address(this), address(0), msg.value, minSyMinted);
amountSyMinted += SY.deposit{value: msg.value}(address(this), address(0), msg.value, 0);
}

if (token != address(0)) {
ERC20(token).transferFrom(msg.sender, address(this), amountIn);
ERC20(token).approve(address(SY), amountIn);
amountSyMinted += SY.deposit(address(this), token, amountIn, minSyMinted);
debitNative += amountIn;
ERC20(token).transferFrom(msg.sender, address(this), amountTokenIn);
ERC20(token).approve(address(SY), amountTokenIn);
amountSyMinted += SY.deposit(address(this), token, amountTokenIn, 0);
debitNative += amountTokenIn;
}

if (amountSyMinted < minSyMinted) {
revert InsufficientOutput(amountSyMinted, minSyMinted, amountSyMinted);
revert InsufficientSYMinted(amountSyMinted, minSyMinted);
}

PYIndex index = YT.newIndex();
uint256 amountInWad;
uint256 amountOutWad;
uint256 strike_;

(amountInWad, amountOutWad, amountYtOut, deltaLiquidity, strike_) =
prepareSwapPt(amountSyMinted, block.timestamp, index);
(amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) =
prepareSwapPtIn(amountPtForFlashSwap, block.timestamp, index);

if (amountYtOut < minYtOut) {
revert InsufficientOutput(amountInWad, minYtOut, amountYtOut);
}

_adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index);

// SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender
uint256 ytOut = amountYtOut + (index.assetToSyUp(amountInWad) - amountOutWad);
uint256 ytOut = amountOut + (index.assetToSyUp(amountInWad) - amountOutWad);

// Converts the SY received from minting it into its components PT and YT.
amountYtOut = mintPtYt(ytOut, address(this));
_credit(address(YT), to, amountYtOut);

amountOut = mintPtYt(ytOut, address(this));

if (amountOut < minYtOut) {
revert InsufficientOutput(amountInWad, minYtOut, amountOut);
}

_credit(address(YT), to, amountOut);

uint256 debitSurplus = address(this).balance;

if (debitSurplus > 0) {
_sendETH(to, debitSurplus);
}

emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountYtOut, deltaLiquidity);
emit Swap(msg.sender, to, address(SY), address(YT), debitNative - debitSurplus, amountOut, deltaLiquidity);
}

function swapExactPtForSy(uint256 amountIn, uint256 minAmountOut, address to)
Expand All @@ -180,7 +218,7 @@ contract RMM is ERC20 {
uint256 strike_;

(amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) =
prepareSwapPt(amountIn, block.timestamp, index);
prepareSwapPtIn(amountIn, block.timestamp, index);

if (amountOut < minAmountOut) {
revert InsufficientOutput(amountInWad, minAmountOut, amountOut);
Expand All @@ -205,7 +243,7 @@ contract RMM is ERC20 {
uint256 strike_;

(amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) =
prepareSwapSy(amountIn, block.timestamp, index);
prepareSwapSyIn(amountIn, block.timestamp, index);

if (amountOut < minAmountOut) {
revert InsufficientOutput(amountInWad, minAmountOut, amountOut);
Expand Down Expand Up @@ -245,50 +283,6 @@ contract RMM is ERC20 {
emit Swap(msg.sender, to, address(PT), address(SY), debitNative, creditNative, deltaLiquidity);
}

/// @dev Swaps SY for YT, sending at least `minAmountOut` YT to `to`.
/// @notice `amountIn` is an amount of PT that needs to be minted from the SY in and the SY flash swapped from the pool
function swapExactSyForYt(uint256 amountIn, uint256 minAmountOut, address to)
external
payable
lock
returns (uint256 amountOut, int256 deltaLiquidity)
{
PYIndex index = YT.newIndex();
uint256 amountInWad;
uint256 amountOutWad;
uint256 strike_;

(amountInWad, amountOutWad, amountOut, deltaLiquidity, strike_) =
prepareSwapPt(amountIn, block.timestamp, index);

if (amountOut < minAmountOut) {
revert InsufficientOutput(amountInWad, minAmountOut, amountOut);
}

_adjust(-toInt(amountOutWad), toInt(amountInWad), deltaLiquidity, strike_, index);

// SY is needed to cover the minted PT, so we need to debit the delta from the msg.sender
uint256 delta = index.assetToSyUp(amountInWad) - amountOutWad;
uint256 ytOut = amountOut + delta;
(uint256 debitNative) = _debit(address(SY), delta);

amountOut = mintPtYt(ytOut, address(this));
_credit(address(YT), to, amountOut);

emit Swap(msg.sender, to, address(SY), address(YT), debitNative, amountOut, deltaLiquidity);
}

function swapExactYtForPt(uint256 exactPtToFlashswap) external lock {
/*
1. Flash swap exact same amount of Yt in PT
2. PT + YT -> SY
3. Sell all SY for PT
4.
*/
}

/// todo: should allocates be executed on the stale curve? I dont think the curve should be updated in allocates.
function allocate(uint256 deltaX, uint256 deltaY, uint256 minLiquidityOut, address to)
external
Expand Down Expand Up @@ -451,7 +445,7 @@ contract RMM is ERC20 {
uint256 max = initialGuess;
for (uint256 iter = 0; iter < 100; ++iter) {
uint256 guess = (min + max) / 2;
(,, uint256 amountOut,,) = prepareSwapPt(guess, blockTime, index);
(,, uint256 amountOut,,) = prepareSwapPtIn(guess, blockTime, index);
uint256 netSyToPt = index.assetToSyUp(guess);

uint256 netSyToPull = netSyToPt - amountOut;
Expand Down Expand Up @@ -525,7 +519,7 @@ contract RMM is ERC20 {
deltaLiquidity = toInt(nextLiquidity) - toInt(totalLiquidity);
}

function prepareSwapSy(uint256 amountIn, uint256 timestamp, PYIndex index)
function prepareSwapSyIn(uint256 amountIn, uint256 timestamp, PYIndex index)
public
view
returns (uint256 amountInWad, uint256 amountOutWad, uint256 amountOut, int256 deltaLiquidity, uint256 strike_)
Expand All @@ -551,7 +545,7 @@ contract RMM is ERC20 {
deltaLiquidity = toInt(nextLiquidity) - toInt(totalLiquidity);
}

function prepareSwapPt(uint256 ptIn, uint256 timestamp, PYIndex index)
function prepareSwapPtIn(uint256 ptIn, uint256 timestamp, PYIndex index)
public
view
returns (uint256 amountInWad, uint256 amountOutWad, uint256 amountOut, int256 deltaLiquidity, uint256 strike_)
Expand Down
2 changes: 2 additions & 0 deletions src/lib/RmmErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ error InsufficientPayment(address token, uint256 actual, uint256 expected);
error InsufficientLiquidityOut(uint256 deltaX, uint256 deltaY, uint256 minLiquidity, uint256 liquidity);
/// @dev Thrown when a swap does not output enough tokens.
error InsufficientOutput(uint256 amountIn, uint256 minAmountOut, uint256 amountOut);
/// @dev Thrown when a swap does not mint sufficient SY tokens given the minimum amount.
error InsufficientSYMinted(uint256 amountMinted, uint256 minAmountMinted);
/// @dev Thrown when a swap expects greater input than is allowed
error ExcessInput(uint256 amountOut, uint256 maxAmountIn, uint256 amountIn);
/// @dev Thrown when an allocate would reduce the liquidity.
Expand Down
16 changes: 8 additions & 8 deletions test/unit/RMM.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ contract RMMTest is Test {
function test_basic_solve_x() public basic {
PYIndex index = YT.newIndex();
uint256 deltaSy = 1 ether;
(,, uint256 approximatedDeltaY,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index);
(,, uint256 approximatedDeltaY,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index);

uint256 proportionalLGivenX = deltaSy * subject().totalLiquidity() / subject().reserveX();
uint256 proportionalLGivenY = approximatedDeltaY * subject().totalLiquidity() / subject().reserveY();
Expand Down Expand Up @@ -276,7 +276,7 @@ contract RMMTest is Test {
function test_swapSy_over_time_basic() public basic {
PYIndex index = YT.newIndex();
uint256 deltaSy = 1 ether;
(,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index);
(,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index);
deal(address(subject().PT()), address(subject()), 1 ether);
deal(address(subject().SY()), address(this), deltaSy);
SY.approve(address(subject()), deltaSy);
Expand Down Expand Up @@ -338,7 +338,7 @@ contract RMMTest is Test {
function test_swapSy_basic() public basic {
PYIndex index = YT.newIndex();
uint256 deltaSy = 1 ether;
(,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index);
(,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index);
deal(address(PT), address(subject()), minAmountOut);
deal(address(SY), address(this), deltaSy);
SY.approve(address(subject()), deltaSy);
Expand Down Expand Up @@ -378,7 +378,7 @@ contract RMMTest is Test {
function test_swapSy_reverts_InsufficientOutput() public basic {
PYIndex index = YT.newIndex();
uint256 deltaSy = 1 ether;
(,, uint256 minAmountOut,,) = subject().prepareSwapSy(deltaSy, block.timestamp, index);
(,, uint256 minAmountOut,,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index);
deal(address(PT), address(subject()), minAmountOut);
deal(address(SY), address(this), deltaSy);
SY.approve(address(subject()), deltaSy);
Expand All @@ -394,7 +394,7 @@ contract RMMTest is Test {
function test_swapSy_event() public basic {
PYIndex index = YT.newIndex();
uint256 deltaSy = 1 ether;
(,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapSy(deltaSy, block.timestamp, index);
(,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapSyIn(deltaSy, block.timestamp, index);
deal(address(PT), address(subject()), minAmountOut);
deal(address(SY), address(this), deltaSy);
SY.approve(address(subject()), deltaSy);
Expand All @@ -407,7 +407,7 @@ contract RMMTest is Test {
function test_swapY() public basic {
PYIndex index = YT.newIndex();
uint256 deltaPt = 1 ether;
(,, uint256 minAmountOut,,) = subject().prepareSwapPt(deltaPt, block.timestamp, index);
(,, uint256 minAmountOut,,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index);
deal(address(SY), address(subject()), minAmountOut);
deal(address(PT), address(this), deltaPt);
PT.approve(address(subject()), deltaPt);
Expand Down Expand Up @@ -444,7 +444,7 @@ contract RMMTest is Test {
function test_swapPt_reverts_InsufficientOutput() public basic {
PYIndex index = YT.newIndex();
uint256 deltaPt = 1 ether;
(,, uint256 minAmountOut,,) = subject().prepareSwapPt(deltaPt, block.timestamp, index);
(,, uint256 minAmountOut,,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index);

mintSY(minAmountOut);
mintPtYt(deltaPt);
Expand All @@ -460,7 +460,7 @@ contract RMMTest is Test {
function test_swapPt_event() public basic {
PYIndex index = YT.newIndex();
uint256 deltaPt = 1 ether;
(,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapPt(deltaPt, block.timestamp, index);
(,, uint256 minAmountOut, int256 delLiq,) = subject().prepareSwapPtIn(deltaPt, block.timestamp, index);
deal(address(SY), address(subject()), minAmountOut);
deal(address(PT), address(this), deltaPt);
PT.approve(address(subject()), deltaPt);
Expand Down
Loading

0 comments on commit ea2534f

Please sign in to comment.