Skip to content

Commit

Permalink
fix flashswap sell logic; add back swapcore interface
Browse files Browse the repository at this point in the history
  • Loading branch information
bill-clippy committed Jan 31, 2024
1 parent b5791a2 commit 23adaed
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 4 deletions.
33 changes: 33 additions & 0 deletions contracts/fund/PrimaryMarketV5.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,39 @@ contract PrimaryMarketV5 is IPrimaryMarketV5, ReentrancyGuard, ITrancheIndexV2,
underlying = _getRedemption(inQ - feeQ);
}

/// @notice Calculate the amount of QUEEN that can be redeemed for at least the given amount
/// of underlying tokens.
/// @dev The return value may not be the minimum solution due to rounding errors.
/// @param minUnderlying Minimum received underlying amount
/// @return inQ QUEEN amount that should be redeemed
function getRedemptionForUnderlying(
uint256 minUnderlying
) external view override returns (uint256 inQ) {
// Assume:
// minUnderlying * fundEquivalentTotalQ = a * fundUnderlying - b
// a * 1e18 = c * (1e18 - redemptionFeeRate) + d
// where
// a, b, c, d are integers
// 0 <= b < fundUnderlying
// 0 <= d < 1e18 - redemeptionFeeRate
// Then
// inQAfterFee = a
// inQ = c
// getRedemption(inQ).underlying
// = floor((c - floor(c * redemptionFeeRate / 1e18)) * fundUnderlying / fundEquivalentTotalQ)
// = floor(ceil(c * (1e18 - redemptionFeeRate) / 1e18) * fundUnderlying / fundEquivalentTotalQ)
// = floor(((c * (1e18 - redemptionFeeRate) + d) / 1e18) * fundUnderlying / fundEquivalentTotalQ)
// = floor(a * fundUnderlying / fundEquivalentTotalQ)
// => floor((a * fundUnderlying - b) / fundEquivalentTotalQ)
// = minUnderlying
uint256 fundUnderlying = IFundV3(fund).getTotalUnderlying();
uint256 fundEquivalentTotalQ = IFundV3(fund).getEquivalentTotalQ();
uint256 inQAfterFee = minUnderlying.mul(fundEquivalentTotalQ).add(fundUnderlying - 1).div(
fundUnderlying
);
return inQAfterFee.divideDecimal(1e18 - redemptionFeeRate);
}

/// @notice Calculate the result of a split.
/// @param inQ QUEEN amount to be split
/// @return outB Received BISHOP amount
Expand Down
48 changes: 47 additions & 1 deletion contracts/fund/WstETHPrimaryMarketRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import "../interfaces/IPrimaryMarketV5.sol";
import "../interfaces/IFundV3.sol";
import "../interfaces/IWstETH.sol";
import "../interfaces/ITrancheIndexV2.sol";
import "../interfaces/IStableSwap.sol";

contract WstETHPrimaryMarketRouter is ITrancheIndexV2 {
contract WstETHPrimaryMarketRouter is IStableSwapCore, ITrancheIndexV2 {
using SafeERC20 for IERC20;

IPrimaryMarketV5 public immutable primaryMarket;
Expand All @@ -28,6 +29,51 @@ contract WstETHPrimaryMarketRouter is ITrancheIndexV2 {
_tokenB = fund_.tokenB();
}

/// @dev Get redemption with StableSwap getQuoteOut interface.
function getQuoteOut(uint256 baseIn) external view override returns (uint256 quoteOut) {
(quoteOut, ) = primaryMarket.getRedemption(baseIn);
}

/// @dev Get creation for QUEEN with StableSwap getQuoteIn interface.
function getQuoteIn(uint256 baseOut) external view override returns (uint256 quoteIn) {
quoteIn = primaryMarket.getCreationForQ(baseOut);
}

/// @dev Get creation with StableSwap getBaseOut interface.
function getBaseOut(uint256 quoteIn) external view override returns (uint256 baseOut) {
baseOut = primaryMarket.getCreation(quoteIn);
}

/// @dev Get redemption for underlying with StableSwap getBaseIn interface.
function getBaseIn(uint256 quoteOut) external view override returns (uint256 baseIn) {
baseIn = primaryMarket.getRedemptionForUnderlying(quoteOut);
}

/// @dev Create QUEEN with StableSwap buy interface.
/// Underlying should have already been sent to this contract
function buy(
uint256 version,
uint256 baseOut,
address recipient,
bytes calldata
) external override returns (uint256 realBaseOut) {
uint256 routerQuoteBalance = IERC20(_wstETH).balanceOf(address(this));
IERC20(_wstETH).safeTransfer(address(primaryMarket), routerQuoteBalance);
realBaseOut = primaryMarket.create(recipient, baseOut, version);
}

/// @dev Redeem QUEEN with StableSwap sell interface.
/// QUEEN should have already been sent to this contract
function sell(
uint256 version,
uint256 quoteOut,
address recipient,
bytes calldata
) external override returns (uint256 realQuoteOut) {
uint256 routerBaseBalance = fund.trancheBalanceOf(TRANCHE_Q, address(this));
realQuoteOut = primaryMarket.redeem(recipient, routerBaseBalance, quoteOut, version);
}

function create(
address recipient,
bool needWrap,
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/IPrimaryMarketV5.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface IPrimaryMarketV5 {

function getRedemption(uint256 inQ) external view returns (uint256 underlying, uint256 fee);

function getRedemptionForUnderlying(uint256 minUnderlying) external view returns (uint256 inQ);

function getSplit(uint256 inQ) external view returns (uint256 outB, uint256 outR);

function getSplitForR(uint256 minOutR) external view returns (uint256 inQ, uint256 outB);
Expand Down
5 changes: 4 additions & 1 deletion contracts/swap/FlashSwapRouterV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,12 @@ contract FlashSwapRouterV3 is ITranchessSwapCallee, ITrancheIndexV2, Ownable {
version
);
// Redeem or swap QUEEN for underlying
uint256 underlyingAmount = IStableSwapCoreInternalRevertExpected(
queenSwapOrPrimaryMarketRouter
).getQuoteOut(outQ);
uint256 totalQuoteAmount = IStableSwapCoreInternalRevertExpected(
queenSwapOrPrimaryMarketRouter
).sell(version, 0, address(this), "");
).sell(version, underlyingAmount, address(this), "");
// Send back quote asset to tranchess swap
IERC20(tokenQuote).safeTransfer(msg.sender, quoteAmount);
// Send the rest of quote asset to user
Expand Down
4 changes: 2 additions & 2 deletions test/flashSwapRouterV3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe("FlashSwapRouterV3", function () {
BigNumber.from(1).shl(256).sub(1),
true
);
const PrimaryMarketRouter = await ethers.getContractFactory("PrimaryMarketRouter");
const PrimaryMarketRouter = await ethers.getContractFactory("WstETHPrimaryMarketRouter");
const primaryMarketRouter = await PrimaryMarketRouter.connect(owner).deploy(
primaryMarket.address
);
Expand Down Expand Up @@ -183,7 +183,7 @@ describe("FlashSwapRouterV3", function () {
await steth.approve(wsteth.address, initWstETH);
await wsteth.wrap(initWstETH);
await wsteth.approve(primaryMarketRouter.address, initWstETH);
await primaryMarketRouter.create(owner.address, initWstETH, 0, 0);
await primaryMarketRouter.create(owner.address, false, initWstETH, 0, 0);
await primaryMarket.split(owner.address, initQ, 0);
await fund.trancheApprove(TRANCHE_B, swapRouter.address, LP_B, 0);
await steth.mint(owner.address, LP_STETH);
Expand Down

0 comments on commit 23adaed

Please sign in to comment.