Skip to content

Commit

Permalink
Simulation tests and preview functions accuracy tests (#83)
Browse files Browse the repository at this point in the history
* fix: set offset factor when deploying

* feat: base contract for integration tests

* feat: e2e simulation

* wip: measure accuracy of preview functions

* chore: add comments

* fix: deploy scripts constructors

* fix: use `assets.collateral` in `convertCollateralToUnderlying`

* feat: handle surplus equity in `rebalanceBeforeWithdraw`

* Enforces a reversion in case the `minForRebalance` limit is breached (#88)

* feat: add check for over exposure

* build: update failing tests

* build: add test case for `RatioOutsideRange`

* build: remove duplicate test file

* Reversion due to high slippage in `redeem` and `deposit` (#85)

* feat: extend `SwapperStorage` to contain oracles and slippages for tokens

* feat: setters/getters for new `Swapper` storage variables

* feat: add slippage limit check

* style: reorder functions

* feat: alter how max slippage works

* style: forge fmt

* build: setter tests for new swapper functions

* fix: minor issue in accounting for `fromAmount` in swap

* build: add slippage to `SwapAdapterMock` and test slippage check in `Swapper`

* feat: additional parameters in initializers

* build: update tests

* fix: maxDeviationUSD calclulation

* style: forge fmt

* build: test slippageTooHigh setup WIP

* style: forge fmt

* build: test `rebalanceUp` reversion when slippage is too high

* fix: remove commented code

* WIP: test helper

* build: testFail case for `rebalanceDownToDebt` when slippage is too high

* style: forge fmt

* build: setup context for testing `MaxSlippageExceeded` reversion in tests

* build: tests for slippage reversion in `deposit` and `redeem`

* fix: typo, deposit reversion test case

* Update fork block number and adjust test cases (#84)

* build: update fork block number constant

* build: update supply/borrow caps in lending pool

* fix: WIP debugging LoanLogic tests

* fix: `_changeSupplyAndBorrowCap` typo

* style: forge fmt

* fix: add `CbETH` supply and borrow caps in `LoopStrategy` constructor

* fix: order of operations in convertUSDToAsset conversion

* fix: previewRedeem tests precision

* build: increase gap of slippage in slippage test

* natspec: extend comment

* fix: increase slippage in mockadapter

* style: forge fmt

---------

Co-authored-by: DuXXuD <duxxud@gmail.com>

* build: add capacity for `SwapperMock` to have real/estimated offsets for testing

* build: add tests for surplus equity due to swap

* fix: remove console logs

* fix: deploy config change

* feat: add integration base test

* fix: move out preview accuracy testing

* fix: handle maxSlippageUSD = 0

* chore: renaming

* chore: move scenarios to unit folder

* chore: remove unused imports

* chore: gitignore simulation output

* fix: moving scenario tests

* fix: moving scenario tests

* chore: imports fix

* upd gitignore

* gitignore: ignore simulation output

* fix: import console

* fix: gitignore test failing without dir

* add gitignore simulation output back

---------

Co-authored-by: NouDaimon <peric.iason@gmail.com>
Co-authored-by: NouDaimon <81045441+NouDaimon@users.noreply.github.com>
  • Loading branch information
3 people committed Jan 24, 2024
1 parent 58a2eba commit 697b389
Show file tree
Hide file tree
Showing 18 changed files with 662 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .env.samle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Base mainnet RPC, used in tests on a forked mainnet
BASE_MAINNET_RPC_URL=
# Tenderly base fork used for deployments to testnet
TENDERLY_RPC_FORK=
FORK_RPC=
# Deployer private key
DEPLOYER_PK=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ out/
# pnpm
pnpm-lock.yaml

# simulation output
test/integration/output
32 changes: 20 additions & 12 deletions deploy/DeployTenderlyFork.s.sol → deploy/DeployFork.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

pragma solidity 0.8.21;

import "forge-std/script.sol";
import { TenderlyForkConfig } from "./config/TenderlyForkConfig.sol";
import { Script } from "forge-std/Script.sol";
import { DeployForkConfig } from "./config/DeployForkConfig.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { ERC1967Proxy } from
"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
Expand Down Expand Up @@ -33,15 +33,16 @@ import { LoopStrategy, ILoopStrategy } from "../src/LoopStrategy.sol";
import { WrappedTokenAdapter } from
"../src/swap/adapter/WrappedTokenAdapter.sol";
import { AerodromeAdapter } from "../src/swap/adapter/AerodromeAdapter.sol";
import "forge-std/console.sol";

/// @title DeployTenderlyFork
/// @title DeployFork
/// @notice Deploys and setups all contracts needed for ILM LoopStrategy, when collateral is CbETH and borrow asset is WETH
/// @notice Made for using on fork of the Base Mainnet.
/// @notice Assumes that deployer has roles for the Seamless pool configuration (ACL_ADMIN and POOL_ADMIN)
/// @notice To obtain roles on the fork, run the simulation on Tenderly UI.
/// @dev deploy with the command:
/// @dev forge script ./deploy/DeployTenderlyFork.s.sol --rpc-url ${TENDERLY_FORK_RPC} --broadcast --slow --delay 20 --force
contract DeployTenderlyFork is Script, TenderlyForkConfig {
/// @dev forge script ./deploy/DeployFork.s.sol --rpc-url ${FORK_RPC} --broadcast --slow --delay 20 --force
contract DeployForkScript is Script, DeployForkConfig {
IERC20 public constant CbETH = IERC20(BASE_MAINNET_CbETH);
IERC20 public constant WETH = IERC20(BASE_MAINNET_WETH);
IPoolAddressesProvider public constant poolAddressesProvider = IPoolAddressesProvider(SEAMLESS_ADDRESS_PROVIDER_BASE_MAINNET);
Expand All @@ -56,8 +57,7 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {
LoopStrategy public strategy;

function run() public {
deployerPrivateKey = vm.envUint("DEPLOYER_PK");
deployerAddress = vm.addr(deployerPrivateKey);
_setDeployer(vm.envUint("DEPLOYER_PK"));

_deployWrappedCbETH();
_setupWrappedCbETH();
Expand All @@ -72,6 +72,11 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {
_setupRoles();
}

function _setDeployer(uint256 _deployerPrivateKey) internal {
deployerPrivateKey = _deployerPrivateKey;
deployerAddress = vm.addr(deployerPrivateKey);
}

function _logAddress(string memory _name, address _address) internal view {
console.log("%s: %s", _name, _address);
}
Expand Down Expand Up @@ -141,7 +146,9 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {
address(swapperImplementation),
abi.encodeWithSelector(
Swapper.Swapper_init.selector,
deployerAddress
deployerAddress,
IPriceOracleGetter(poolAddressesProvider.getPriceOracle()),
swapperOffsetDeviation
)
);

Expand All @@ -159,8 +166,7 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {

// WrappedCbETH Adapter
wrappedTokenAdapter = new WrappedTokenAdapter();
wrappedTokenAdapter.WrappedTokenAdapter__Init(deployerAddress);
wrappedTokenAdapter.setSwapper(address(swapper));
wrappedTokenAdapter.WrappedTokenAdapter__Init(deployerAddress, address(swapper));
wrappedTokenAdapter.setWrapper(
CbETH,
IERC20(address(wrappedCbETH)),
Expand All @@ -171,9 +177,8 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {
// CbETH <-> WETH Aerodrome Adapter
aerodromeAdapter = new AerodromeAdapter();
aerodromeAdapter.AerodromeAdapter__Init(
deployerAddress, AERODROME_ROUTER, AERODROME_FACTORY
deployerAddress, AERODROME_ROUTER, AERODROME_FACTORY, address(swapper)
);
aerodromeAdapter.setSwapper(address(swapper));

IRouter.Route[] memory routesCbETHtoWETH = new IRouter.Route[](1);
routesCbETHtoWETH[0] = IRouter.Route({
Expand Down Expand Up @@ -213,7 +218,10 @@ contract DeployTenderlyFork is Script, TenderlyForkConfig {

vm.startBroadcast(deployerPrivateKey);
swapper.setRoute(IERC20(address(wrappedCbETH)), WETH, stepsWrappedToWETH);
swapper.setOffsetFactor(IERC20(address(wrappedCbETH)), WETH, swapperOffsetFactor);

swapper.setRoute(WETH, IERC20(address(wrappedCbETH)), stepsWETHtoWrapped);
swapper.setOffsetFactor(WETH, IERC20(address(wrappedCbETH)), swapperOffsetFactor);
vm.stopBroadcast();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import { BaseMainnetConstants } from "./BaseMainnetConstants.sol";
import { CollateralRatio } from "../../src/types/DataTypes.sol";
import { USDWadRayMath } from "../../src/libraries/math/USDWadRayMath.sol";

/// @title TenderlyForkConfig
/// @notice configuration and constants used on Tenderly fork deployment
abstract contract TenderlyForkConfig is BaseMainnetConstants {
/// @title DeployForkConfig
/// @notice configuration and constants used on fork deployment
abstract contract DeployForkConfig is BaseMainnetConstants {

string public constant STRATEGY_ERC20_NAME = "CbETH/WETH Seamless ILM";
string public constant STRATEGY_ERC20_SYMBOL = "ilmCbETH";

CollateralRatio public collateralRatioTargets = CollateralRatio({
target: USDWadRayMath.usdDiv(300, 200), // 3x
minForRebalance: USDWadRayMath.usdDiv(400, 300), // 4x
minForRebalance: USDWadRayMath.usdDiv(330, 230), // 3.3x
maxForRebalance: USDWadRayMath.usdDiv(270, 170), // 2.7x
maxForDepositRebalance: USDWadRayMath.usdDiv(301, 201), // 3.01x
minForWithdrawRebalance: USDWadRayMath.usdDiv(299, 199) // 2.99x
maxForDepositRebalance: USDWadRayMath.usdDiv(299, 199), // 2.99x
minForWithdrawRebalance: USDWadRayMath.usdDiv(301, 201) // 3.01x
});

uint256 public constant ratioMargin = 10 ** 4; // 0.01% ratio margin
Expand All @@ -35,4 +35,7 @@ abstract contract TenderlyForkConfig is BaseMainnetConstants {
uint256 public constant wrappedCbETH_LTV = 90_00;
uint256 public constant wrappedCbETH_LiquidationTrashold = 92_00;
uint256 public constant wrappedCbETH_LiquidationBonus = 100_00 + 5_00;

uint256 public constant swapperOffsetFactor = 750000; // 0.75 %
uint256 public constant swapperOffsetDeviation = 99000000; // 99%
}
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ solc = "0.8.21"
block_number = 5950437
verbosity = 2
evm_version = "paris"
fs_permissions = [{ access = "write", path = "./"}]

[fmt]
line_length = 80
Expand Down
2 changes: 1 addition & 1 deletion src/LoopStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ contract LoopStrategy is
uint256 collateralAmountAsset
) internal virtual returns (uint256 underlyingAmountAsset) {
if (assets.underlying != assets.collateral) {
IWrappedERC20PermissionedDeposit(address(assets.underlying))
IWrappedERC20PermissionedDeposit(address(assets.collateral))
.withdraw(collateralAmountAsset);
}
underlyingAmountAsset = collateralAmountAsset;
Expand Down
6 changes: 4 additions & 2 deletions src/libraries/RebalanceLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ library RebalanceLogic {

// shares lose equity equal to the amount of equity lost for
// the rebalance to pay the adjusted debt
shareEquityUSD -=
initialEquityUSD - (state.collateralUSD - state.debtUSD);
if (initialEquityUSD > (state.collateralUSD - state.debtUSD)) {
shareEquityUSD -=
initialEquityUSD - (state.collateralUSD - state.debtUSD);
}
}

// convert equity to collateral asset
Expand Down
7 changes: 6 additions & 1 deletion src/swap/Swapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,14 @@ contract Swapper is ISwapper, AccessControlUpgradeable, UUPSUpgradeable {
uint256 maxSlippageUSD = fromAmountUSD.usdMul(
offsetUSD + maxDeviationUSD
).usdDiv(USDWadRayMath.USD);

// for the very low amounts allow 1 wei of slippage
if (maxSlippageUSD == 0) {
maxSlippageUSD = 1;
}

if (fromAmountUSD - maxSlippageUSD > toAmountUSD) {
revert MaxSlippageExceeded();
}
}
}
}
16 changes: 16 additions & 0 deletions test/config/TestConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ abstract contract TestConstants {
address public constant AERODROME_FACTORY =
0x420DD381b31aEf6683db6B902084cB0FFECe40Da;

address public constant CHAINLINK_CBETH_USD_ORACLE =
0xd7818272B9e248357d13057AAb0B417aF31E817d;

// USDbC has 6 decimals
uint256 public constant ONE_USDbC = 1e6;

address public constant SEAMLESS_ATOKEN_IMPL =
0x27076A995387458da63b23d9AFe3df851727A8dB;
address public constant SEAMLESS_STABLE_DEBT_TOKEN_IMPL =
0xb4D5e163738682A955404737f88FDCF15C1391bF;
address public constant SEAMLESS_VARIABLE_DEBT_TOKEN_IMPL =
0x3800DA378e17A5B8D07D0144c321163591475977;
address public constant SEAMLESS_CBETH_INTEREST_RATE_STRATEGY_ADDRESS =
0xcEd653F5C689eC80881b1A8b9Ab2b64DF2B963Bd;
address public constant SEAMLESS_TREASURY =
0x982F3A0e3183896f9970b8A9Ea6B69Cd53AF1089;
address public constant SEAMLESS_INCENTIVES_CONTROLLER =
0x91Ac2FfF8CBeF5859eAA6DdA661feBd533cD3780;
}
44 changes: 44 additions & 0 deletions test/integration/IntegrationBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.21;

import { Test } from "forge-std/Test.sol";

import { IACLManager } from "@aave/contracts/interfaces/IACLManager.sol";
import { DeployForkScript } from "../../deploy/DeployFork.s.sol";
import { VmSafe } from "forge-std/Vm.sol";

/// @notice Setup contract for the integration tests
/// @notice deploys all related contracts on the fork, and setup lending pool parameters
contract IntegrationBase is Test, DeployForkScript {

string internal BASE_RPC_URL = vm.envString("BASE_MAINNET_RPC_URL");

VmSafe.Wallet public testDeployer = vm.createWallet("deployer");

function setUp() public virtual {
vm.createSelectFork(BASE_RPC_URL);

_setDeployer(testDeployer.privateKey);

address aclAdmin = poolAddressesProvider.getACLAdmin();
vm.startPrank(aclAdmin);
IACLManager(poolAddressesProvider.getACLManager()).addPoolAdmin(
testDeployer.addr
);
poolAddressesProvider.setACLAdmin(testDeployer.addr);
vm.stopPrank();

_deployWrappedCbETH();
_setupWrappedCbETH();
_setupWETHborrowCap();

_deploySwapper();
_deploySwapAdapters();
_setupSwapperRoutes();

_deployLoopStrategy();

_setupRoles();
}
}
28 changes: 28 additions & 0 deletions test/integration/IntegrationBaseTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.21;

import { Test } from "forge-std/Test.sol";

import { IntegrationBase } from "./IntegrationBase.sol";

/// @notice Test confirming deployment is done correctly and we can deposit and redeem funds
contract IntegrationBaseTest is IntegrationBase {

/// @dev test confirming deployment is done correctly and we can deposit and redeem funds
function test_integrationBaseTest() public {
address user = makeAddr("user");

uint256 amount = 1 ether;

vm.startPrank(user);
deal(address(CbETH), user, amount);
CbETH.approve(address(strategy), amount);

uint256 shares = strategy.deposit(amount, user);

strategy.redeem(shares / 2, user, user);

vm.stopPrank();
}
}

0 comments on commit 697b389

Please sign in to comment.