Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes bad slippage usage in the MM #139

Merged
merged 5 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 23 additions & 54 deletions contracts/AMM/BalancerAMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,69 +128,42 @@ contract BalancerAMM {

/**
* @notice Buy Sweep
* @param tokenAddress Token Address to use for buying sweep.
* @param tokenAmount Token Amount.
* @param usdxAddress Token Address to use for buying sweep.
* @param usdxAmount Token Amount.
* @param amountOutMin Minimum amount out.
* @dev Increases the sweep balance and decrease collateral balance.
*/
function buySweep(
address tokenAddress,
uint256 tokenAmount,
uint256 amountOutMin
) external returns (uint256 sweepAmount) {
bool lowerPriceInPool = true;

if (address(marketMaker) != address(0)) {
uint256 buyPrice = marketMaker.getBuyPrice();
if (buyPrice < getPrice()) {
lowerPriceInPool = false;
sweepAmount = (tokenAmount * (10 ** sweep.decimals())) / buyPrice;
uint256 slippage = (sweepAmount * (10 ** base.decimals()) / amountOutMin) - (10 ** base.decimals());

TransferHelper.safeTransferFrom(address(base), msg.sender, address(this), tokenAmount);
TransferHelper.safeApprove(address(base), address(marketMaker), tokenAmount);
marketMaker.buySweep(sweepAmount, slippage);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);
}
}

if(lowerPriceInPool) {
checkRate(tokenAddress, tokenAmount, amountOutMin);
sweepAmount = swap(
tokenAddress,
address(sweep),
tokenAmount,
amountOutMin,
address(pool)
);
}

emit Bought(tokenAmount);
function buySweep(address usdxAddress, uint256 usdxAmount, uint256 amountOutMin)
external returns (uint256 sweepAmount)
{
emit Bought(usdxAmount);

if (address(marketMaker) != address(0) && marketMaker.getBuyPrice() < getPrice()) {
TransferHelper.safeTransferFrom(address(base), msg.sender, address(this), usdxAmount);
TransferHelper.safeApprove(address(base), address(marketMaker), usdxAmount);
sweepAmount = marketMaker.buySweep(usdxAmount);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);
} else {
checkRate(usdxAddress, usdxAmount, amountOutMin);
sweepAmount = swap(usdxAddress, address(sweep), usdxAmount, amountOutMin, address(pool));
}
}

/**
* @notice Sell Sweep
* @param tokenAddress Token Address to return after selling sweep.
* @param usdxAddress Token Address to return after selling sweep.
* @param sweepAmount Sweep Amount.
* @param amountOutMin Minimum amount out.
* @dev Decreases the sweep balance and increase collateral balance
*/
function sellSweep(
address tokenAddress,
address usdxAddress,
uint256 sweepAmount,
uint256 amountOutMin
) external returns (uint256 tokenAmount) {
checkRate(tokenAddress, amountOutMin, sweepAmount);

tokenAmount = swap(
address(sweep),
tokenAddress,
sweepAmount,
amountOutMin,
address(pool)
);

emit Sold(sweepAmount);
checkRate(usdxAddress, amountOutMin, sweepAmount);
tokenAmount = swap(address(sweep), usdxAddress, sweepAmount, amountOutMin, address(pool));
}

/**
Expand Down Expand Up @@ -240,13 +213,9 @@ contract BalancerAMM {
* @param amountOutMin Minimum amount out.
* @param poolAddress The pool to execute the swap into
*/
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address poolAddress
) public returns (uint256 amountOut) {
function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin, address poolAddress)
private returns (uint256 amountOut)
{
bytes32 poolId = IBalancerPool(poolAddress).getPoolId();
address vaultAddress = IBalancerPool(poolAddress).getVault();

Expand Down
90 changes: 36 additions & 54 deletions contracts/AMM/UniswapAMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ contract UniswapAMM {
event Sold(uint256 sweepAmount);

// Errors
error ZeroAmount();
error BadRate();
error NotOwnerOrGov();

modifier onlyOwner () {
Expand Down Expand Up @@ -135,65 +137,44 @@ contract UniswapAMM {

/**
* @notice Buy Sweep
* @param tokenAddress Token Address to use for buying sweep.
* @param tokenAmount Token Amount.
* @param usdxAddress Token Address to use for buying sweep.
* @param usdxAmount Token Amount.
* @param amountOutMin Minimum amount out.
* @dev Increases the sweep balance and decrease collateral balance.
*/
function buySweep(
address tokenAddress,
uint256 tokenAmount,
uint256 amountOutMin
) external returns (uint256 sweepAmount) {
bool lowerPriceInPool = true;

if (address(marketMaker) != address(0)) {
uint256 buyPrice = marketMaker.getBuyPrice();
if (buyPrice < getPrice()) {
lowerPriceInPool = false;
sweepAmount = (tokenAmount * (10 ** sweep.decimals())) / buyPrice;
uint256 slippage = (sweepAmount * (10 ** base.decimals()) / amountOutMin) - (10 ** base.decimals());

TransferHelper.safeTransferFrom(address(base), msg.sender, address(this), tokenAmount);
TransferHelper.safeApprove(address(base), address(marketMaker), tokenAmount);
marketMaker.buySweep(sweepAmount, slippage);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);
}
}

if(lowerPriceInPool) {
sweepAmount = swap(
tokenAddress,
address(sweep),
tokenAmount,
amountOutMin,
pool
);
function buySweep(address usdxAddress, uint256 usdxAmount, uint256 amountOutMin)
external returns (uint256 sweepAmount)
{
if (address(marketMaker) != address(0) && marketMaker.getBuyPrice() < getPrice() ) {
TransferHelper.safeTransferFrom(address(base), msg.sender, address(this), usdxAmount);
TransferHelper.safeApprove(address(base), address(marketMaker), usdxAmount);
sweepAmount = marketMaker.buySweep(usdxAmount);
TransferHelper.safeTransfer(address(sweep), msg.sender, sweepAmount);
} else {
checkRate(usdxAddress, usdxAmount, amountOutMin);
sweepAmount = swap(usdxAddress, address(sweep), usdxAmount, amountOutMin, pool);
}

emit Bought(tokenAmount);
emit Bought(usdxAmount);
}

/**
* @notice Sell Sweep
* @param tokenAddress Token Address to return after selling sweep.
* @param usdxAddress Token Address to return after selling sweep.
* @param sweepAmount Sweep Amount.
* @param amountOutMin Minimum amount out.
* @dev Decreases the sweep balance and increase collateral balance
*/
function sellSweep(
address tokenAddress,
uint256 sweepAmount,
uint256 amountOutMin
) external returns (uint256 tokenAmount) {
function sellSweep(address usdxAddress, uint256 sweepAmount, uint256 amountOutMin)
external returns (uint256 tokenAmount)
{
emit Sold(sweepAmount);
tokenAmount = swap(
address(sweep),
tokenAddress,
sweepAmount,
amountOutMin,
pool
);
checkRate(usdxAddress, amountOutMin, sweepAmount);
tokenAmount = swap(address(sweep), usdxAddress, sweepAmount, amountOutMin, pool);
}

function setMarketMaker(address _marketMaker) external onlyOwner {
marketMaker = IMarketMaker(_marketMaker);
}

/**
Expand All @@ -204,13 +185,9 @@ contract UniswapAMM {
* @param amountOutMin Minimum amount out.
* @param poolAddress Pool to use in the swap
*/
function swap(
address tokenA,
address tokenB,
uint256 amountIn,
uint256 amountOutMin,
address poolAddress
) public returns (uint256 amountOut) {
function swap(address tokenA, address tokenB, uint256 amountIn, uint256 amountOutMin, address poolAddress)
private returns (uint256 amountOut)
{
// Approval
TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountIn);
TransferHelper.safeApprove(tokenA, address(ROUTER), amountIn);
Expand All @@ -230,7 +207,12 @@ contract UniswapAMM {
amountOut = ROUTER.exactInputSingle(swapParams);
}

function setMarketMaker(address _marketMaker) external onlyOwner {
marketMaker = IMarketMaker(_marketMaker);
function checkRate(address usdxAddress, uint256 usdxAmount, uint256 sweepAmount) internal view {
if(usdxAmount == 0 || sweepAmount == 0) revert ZeroAmount();
uint256 tokenFactor = 10 ** IERC20Metadata(usdxAddress).decimals();
uint256 sweepFactor = 10 ** sweep.decimals();
uint256 rate = usdxAmount * sweepFactor * 1e6 / (tokenFactor * sweepAmount);

if(rate > 16e5 || rate < 6e5) revert BadRate();
}
}
60 changes: 46 additions & 14 deletions contracts/Assets/ERC20Asset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ pragma solidity 0.8.19;
* @dev Representation of an on-chain investment like Wrapped Ether, Wrapped Bitcoin ...
*/

import "../Stabilizer/Stabilizer.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { Stabilizer, IPriceFeed, IAMM, ChainlinkLibrary, OvnMath, TransferHelper, IERC20Metadata } from "../Stabilizer/Stabilizer.sol";
import { ISwapRouter } from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";

contract ERC20Asset is Stabilizer {
uint16 private constant DEADLINE_GAP = 15 minutes;
ISwapRouter private constant ROUTER = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);

// Variables
IERC20Metadata private immutable token;
IPriceFeed private immutable oracleToken;
address private immutable poolAddress;
IUniswapV3Pool private immutable pool;

// Events
event Invested(uint256 indexed tokenAmount);
Expand All @@ -30,12 +36,12 @@ contract ERC20Asset is Stabilizer {
address _oracleUsdx,
address _oracleToken,
address _borrower,
address _poolAddress
address _pool

) Stabilizer(_name, _sweep, _usdx, _oracleUsdx, _borrower) {
token = IERC20Metadata(_token);
oracleToken = IPriceFeed(_oracleToken);
poolAddress = _poolAddress;
pool = IUniswapV3Pool(_pool);
}

/* ========== Views ========== */
Expand Down Expand Up @@ -108,15 +114,12 @@ contract ERC20Asset is Stabilizer {
if (usdxBalance == 0) revert NotEnoughBalance();
if (usdxBalance < usdxAmount) usdxAmount = usdxBalance;

IAMM _amm = amm();
uint256 usdxInToken = _oracleUsdxToToken(usdxAmount);
TransferHelper.safeApprove(address(usdx), address(_amm), usdxAmount);
uint256 tokenAmount = _amm.swap(
uint256 tokenAmount = swap(
address(usdx),
address(token),
usdxAmount,
OvnMath.subBasisPoints(usdxInToken, slippage),
poolAddress
OvnMath.subBasisPoints(usdxInToken, slippage)
);

emit Invested(tokenAmount);
Expand All @@ -128,22 +131,51 @@ contract ERC20Asset is Stabilizer {
) internal override returns (uint256 divestedAmount) {
uint256 tokenAmount = _oracleUsdxToToken(usdxAmount);
uint256 tokenBalance = token.balanceOf(address(this));
if (tokenBalance == 0) revert NotEnoughBalance();
if (tokenBalance < tokenAmount) tokenAmount = tokenBalance;

IAMM _amm = amm();
uint256 tokenInUsdx = _oracleTokenToUsdx(tokenAmount);
TransferHelper.safeApprove(address(token), address(_amm), tokenAmount);
divestedAmount = _amm.swap(
divestedAmount = swap(
address(token),
address(usdx),
tokenAmount,
OvnMath.subBasisPoints(tokenInUsdx, slippage),
poolAddress
OvnMath.subBasisPoints(tokenInUsdx, slippage)
);

emit Divested(divestedAmount);
}

/**
* @notice Swap tokenA into tokenB using uniV3Router.ExactInputSingle()
* @param tokenA Address to in
* @param tokenB Address to out
* @param amountIn Amount of _tokenA
* @param amountOutMin Minimum amount out.
*/
function swap(
address tokenA,
address tokenB,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256 amountOut) {
// Approval
TransferHelper.safeApprove(tokenA, address(ROUTER), amountIn);

ISwapRouter.ExactInputSingleParams memory swapParams = ISwapRouter
.ExactInputSingleParams({
tokenIn: tokenA,
tokenOut: tokenB,
fee: pool.fee(),
recipient: address(this),
deadline: block.timestamp + DEADLINE_GAP,
amountIn: amountIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
});

amountOut = ROUTER.exactInputSingle(swapParams);
}

function _oracleTokenToUsd(
uint256 tokenAmount
) internal view returns (uint256) {
Expand Down
Loading