Skip to content

Commit

Permalink
Fix/slippage (#122)
Browse files Browse the repository at this point in the history
* fix: slippage

* tests: update tests to have correct oracle value

* feat: checkSlippage only once

* fix: scope some variables os it can build

* chore: skip script tests
  • Loading branch information
0xtekgrinder authored Nov 20, 2024
1 parent 3ea9f59 commit 2fa74b7
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 37 deletions.
27 changes: 15 additions & 12 deletions contracts/helpers/MultiBlockHarvester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BaseHarvester, YieldBearingParams } from "./BaseHarvester.sol";
import { ITransmuter } from "../interfaces/ITransmuter.sol";
import { IAgToken } from "../interfaces/IAgToken.sol";
import { IPool } from "../interfaces/IPool.sol";
import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";

import "../utils/Errors.sol";
import "../utils/Constants.sol";
Expand All @@ -19,6 +20,7 @@ import "../utils/Constants.sol";
/// @dev Contract to harvest yield from multiple yield bearing assets in multiple blocks transactions
contract MultiBlockHarvester is BaseHarvester {
using SafeERC20 for IERC20;
using SafeMath for uint256;

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VARIABLES
Expand Down Expand Up @@ -116,7 +118,6 @@ contract MultiBlockHarvester is BaseHarvester {
address(this),
block.timestamp
);
_checkSlippage(amount, amountOut, yieldBearingInfo.asset, depositAddress, false);
if (yieldBearingAsset == XEVT) {
_adjustAllowance(yieldBearingInfo.asset, address(depositAddress), amountOut);
(uint256 shares, ) = IPool(depositAddress).deposit(amountOut, address(this));
Expand All @@ -129,8 +130,10 @@ contract MultiBlockHarvester is BaseHarvester {
address(this),
block.timestamp
);
_checkSlippage(amount, amountOut, address(agToken), depositAddress, false);
} else if (yieldBearingAsset == USDM) {
IERC20(yieldBearingInfo.asset).safeTransfer(depositAddress, amountOut);
_checkSlippage(amount, amountOut, yieldBearingInfo.asset, depositAddress, false);
}
} else {
uint256 amountOut = transmuter.swapExactInput(
Expand Down Expand Up @@ -161,23 +164,23 @@ contract MultiBlockHarvester is BaseHarvester {
address depositAddress,
bool assetIn
) internal view {
uint256 decimalsAsset = IERC20Metadata(asset).decimals();

// Divide or multiply the amountIn to match the decimals of the asset
amountIn = _scaleAmountBasedOnDecimals(decimalsAsset, 18, amountIn, assetIn);
amountIn = _scaleAmountBasedOnDecimals(IERC20Metadata(asset).decimals(), 18, amountIn, assetIn);

if (asset == USDC || asset == USDM || asset == EURC) {
uint256 result;
if (asset == USDC || asset == USDM || asset == EURC || asset == address(agToken)) {
// Assume 1:1 ratio between stablecoins
unchecked {
uint256 slippage = ((amountIn - amountOut) * 1e9) / amountIn;
if (slippage > maxSlippage) revert SlippageTooHigh();
}
(, result) = amountIn.trySub(amountOut);
} else if (asset == XEVT) {
// Assume 1:1 ratio between the underlying asset of the vault
unchecked {
uint256 slippage = ((IPool(depositAddress).convertToAssets(amountIn) - amountOut) * 1e9) / amountIn;
if (slippage > maxSlippage) revert SlippageTooHigh();
if (assetIn) {
(, result) = IPool(depositAddress).convertToAssets(amountIn).trySub(amountOut);
} else {
(, result) = amountIn.trySub(IPool(depositAddress).convertToAssets(amountOut));
}
} else revert InvalidParam();

uint256 slippage = (result * 1e9) / amountIn;
if (slippage > maxSlippage) revert SlippageTooHigh();
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"description": "",
"scripts": {
"ci:coverage": "forge coverage --report lcov && yarn lcov:clean",
"ci:coverage": "forge coverage --report lcov --no-match-path 'test/scripts/*' && yarn lcov:clean",
"coverage": "FOUNDRY_PROFILE=dev forge coverage --report lcov && yarn lcov:clean && yarn lcov:generate-html",
"compile": "forge build",
"compile:dev": "FOUNDRY_PROFILE=dev forge build",
Expand Down
51 changes: 27 additions & 24 deletions test/fuzz/MultiBlockHarvester.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,22 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {
)
);

oracleXEVT = AggregatorV3Interface(address(new MockChainlinkOracle()));
circuitChainlink[0] = AggregatorV3Interface(oracleXEVT);
readData = abi.encode(circuitChainlink, stalePeriods, circuitChainIsMultiplied, chainlinkDecimals, quoteType);
MockChainlinkOracle(address(oracleXEVT)).setLatestAnswer(int256(BASE_8));
transmuter.setOracle(
XEVT,
abi.encode(
OracleReadType.CHAINLINK_FEEDS,
OracleReadType.STABLE,
readData,
targetData,
abi.encode(uint128(0), uint128(0))
)
);
{
address oracle = 0x6B102047A4bB943DE39233E44487F2d57bDCb33e;
uint256 normalizationFactor = 1e18; // price == 36 decimals
readData = bytes("");
targetData = abi.encode(oracle, normalizationFactor);
transmuter.setOracle(
XEVT,
abi.encode(
OracleReadType.NO_ORACLE,
OracleReadType.MORPHO_ORACLE,
readData,
targetData,
abi.encode(uint128(0), uint128(0))
)
);
}

oracleUSDM = AggregatorV3Interface(address(new MockChainlinkOracle()));
circuitChainlink[0] = AggregatorV3Interface(oracleUSDM);
Expand Down Expand Up @@ -179,6 +181,8 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {
harvester.setYieldBearingToDepositAddress(XEVT, XEVT);
harvester.setYieldBearingToDepositAddress(USDM, receiver);

transmuter.toggleTrusted(address(harvester), TrustedType.Seller);

agToken.mint(address(harvester), 1_000_000e18);

vm.stopPrank();
Expand Down Expand Up @@ -358,7 +362,7 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {
}

function test_harvest_IncreaseExposureXEVT(uint256 amount) external {
amount = 7022;
amount = bound(amount, 1e3, 1e11);
_loadReserve(EURC, amount);
_setYieldBearingData(XEVT, EURC);

Expand Down Expand Up @@ -399,7 +403,6 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {

assertEq(expectedIncrease, 0);
assertEq(issuedFromStablecoinBefore, 0);
assertEq(issuedFromYieldBearingAssetBefore, amount * 1e12);
assertEq(totalIssuedBefore, issuedFromYieldBearingAssetBefore);
assertEq(expectedAmount, issuedFromYieldBearingAssetBefore - ((targetExposure * totalIssuedBefore) / 1e9));

Expand Down Expand Up @@ -520,13 +523,13 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {
}

function test_ComputeRebalanceAmount_HigherThanMaxWithHarvest() external {
_loadReserve(XEVT, 1e11);
_loadReserve(EURC, 1e11);
_loadReserve(USDC, 1e11);
_loadReserve(USDM, 1e23);
uint64 minExposure = uint64((15 * 1e9) / 100);
uint64 maxExposure = uint64((60 * 1e9) / 100);
_setYieldBearingData(XEVT, EURC, minExposure, maxExposure);
_setYieldBearingData(USDM, USDC, minExposure, maxExposure);

(uint8 increase, uint256 amount) = harvester.computeRebalanceAmount(XEVT);
(uint8 increase, uint256 amount) = harvester.computeRebalanceAmount(USDM);
assertEq(amount, (2e23 * uint256(maxExposure)) / 1e9 - 1e23);
assertEq(increase, 0);
}
Expand All @@ -544,13 +547,13 @@ contract MultiBlockHarvestertTest is Fixture, FunctionUtils {
}

function test_ComputeRebalanceAmount_LowerThanMinAfterHarvest() external {
_loadReserve(EURC, 9e10);
_loadReserve(XEVT, 1e10);
_loadReserve(USDC, 9e10);
_loadReserve(USDM, 1e22);
uint64 minExposure = uint64((89 * 1e9) / 100);
uint64 maxExposure = uint64((999 * 1e9) / 1000);
_setYieldBearingData(XEVT, EURC, minExposure, maxExposure);
_setYieldBearingData(USDM, USDC, minExposure, maxExposure);

(uint8 increase, uint256 amount) = harvester.computeRebalanceAmount(XEVT);
(uint8 increase, uint256 amount) = harvester.computeRebalanceAmount(USDM);
assertEq(amount, 9e22 - (1e23 * uint256(minExposure)) / 1e9);
assertEq(increase, 1);
}
Expand Down

0 comments on commit 2fa74b7

Please sign in to comment.