Skip to content

Commit

Permalink
pass initial flashloan test
Browse files Browse the repository at this point in the history
  • Loading branch information
daopunk committed Jul 9, 2024
1 parent bee4acc commit bfa4ed4
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 57 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"test:coverage": "forge coverage --report lcov && lcov --ignore-errors unused --remove lcov.info 'node_modules/*' 'script/*' 'test/*' 'src/contracts/for-test/*' 'src/libraries/*' -o lcov.info.pruned && mv lcov.info.pruned lcov.info && genhtml -o coverage-report lcov.info"
},
"dependencies": {
"@opendollar/contracts": "0.0.0-d805d33e",
"@opendollar/contracts": "0.0.0-984c17c2",
"@openzeppelin-contracts-3.4.2-solc-0.7": "yarn:@openzeppelin/contracts@^3.4.2",
"@openzeppelin/contracts": "4.9.6",
"@paraswap/sdk": "^6.7.0",
Expand Down
6 changes: 6 additions & 0 deletions script/Registry.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ bytes32 constant WSTETH_HASH = bytes32(keccak256('WSTETH'));
bytes32 constant AAVE_RETH_HASH = bytes32(keccak256('AAVE_RETH'));
bytes32 constant AAVE_WETH_HASH = bytes32(keccak256('AAVE_WETH'));
bytes32 constant AAVE_WSTETH_HASH = bytes32(keccak256('AAVE_WSTETH'));

// Oracle Relayers
address constant MAINNET_CHAINLINK_ETH_USD_RELAYER = 0x3e6C1621f674da311E57646007fBfAd857084383;
address constant MAINNET_DENOMINATED_WSTETH_USD_ORACLE = 0xD0cf1FfFF3FB90c87210D76DFBc3AcfFd02D6B12;
address constant MAINNET_DENOMINATED_RETH_USD_ORACLE = 0x2b6b76D9854E9A7189c2F1b496c10043b373e453;
address constant MAINNET_CHAINLINK_ARB_USD_RELAYER = 0x2635f731BB6981E72F92A781578952450759F762;
32 changes: 13 additions & 19 deletions src/leverage/ParaswapSellAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity 0.8.20;

import 'forge-std/Test.sol';

Check warning on line 4 in src/leverage/ParaswapSellAdapter.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

global import of path forge-std/Test.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check warning on line 4 in src/leverage/ParaswapSellAdapter.sol

View workflow job for this annotation

GitHub Actions / Run Linters (18.x)

Import 'forge-std/Test.sol' in contract ParaswapSellAdapter should be declared as import {contract_to_import} from 'forge-std/Test.sol';
import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {IERC20Metadata} from '@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol';
import {FlashLoanSimpleReceiverBase} from '@aave-core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol';
import {IPoolAddressesProvider} from '@aave-core-v3/contracts/interfaces/IPoolAddressesProvider.sol';
Expand All @@ -27,9 +26,11 @@ import {ExitActions} from 'src/leverage/ExitActions.sol';
contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapter, Test {
// using PercentageMath for uint256;
// uint256 public constant MAX_SLIPPAGE_PERCENT = 0.3e4; // 30.00%
uint256 public constant PREMIUM = 500_000_000_000;

IParaSwapAugustusRegistry public immutable AUGUSTUS_REGISTRY;
ODProxy public immutable PS_ADAPTER_ODPROXY;
IERC20Metadata public immutable OPEN_DOLLAR;

IParaswapAugustus public augustus;

Expand All @@ -51,6 +52,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
* @param _coinJoin address of OpenDollar CoinJoin
*/
constructor(
address _systemCoin,
address _augustusRegistry,
address _augustusSwapper,
address _poolProvider,
Expand All @@ -59,6 +61,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
address _collateralJoinFactory,
address _coinJoin
) FlashLoanSimpleReceiverBase(IPoolAddressesProvider(_poolProvider)) {
OPEN_DOLLAR = IERC20Metadata(_systemCoin);
AUGUSTUS_REGISTRY = IParaSwapAugustusRegistry(_augustusRegistry);
augustus = IParaswapAugustus(_augustusSwapper);
IVault721 _v721 = IVault721(_vault721);
Expand Down Expand Up @@ -97,13 +100,11 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
/// @dev approve address(this) as safeHandler and request to borrow asset on Aave
function requestFlashloan(
SellParams memory _sellParams,
uint256 _collateralLoan,
uint256 _minDstAmount,
uint256 _safeId,
bytes32 _cType
) external {
// how much
uint256 _collateralAmount = 1 ether;

// deposit collateral, generate debt
bytes memory _payload = abi.encodeWithSelector(
exitActions.lockTokenCollateralAndGenerateDebtToAccount.selector,
Expand All @@ -112,15 +113,15 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
address(collateralJoinFactory.collateralJoins(_cType)),
coinJoin,
_safeId,
_collateralAmount,
_collateralLoan - PREMIUM,
_sellParams.sellAmount
);

// borrow collateral
POOL.flashLoanSimple({
receiverAddress: address(this),
asset: address(_sellParams.toToken),
amount: _collateralAmount,
amount: _collateralLoan,
params: abi.encode(_minDstAmount, _sellParams, _payload),
referralCode: uint16(block.number)
});
Expand All @@ -137,21 +138,15 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
(uint256 _minDstAmount, SellParams memory _sellParams, bytes memory _payload) =
abi.decode(params, (uint256, SellParams, bytes));

uint256 _beforebalance = IERC20(_sellParams.fromToken).balanceOf(address(this));
uint256 _sellAmount = _sellParams.sellAmount;
emit log_named_uint('RETH BAL AQUIRE LOAN', IERC20Metadata(_sellParams.toToken).balanceOf(address(this)));

emit log_named_uint('RETH BALANCE W LOAN', IERC20(_sellParams.toToken).balanceOf(address(this)));
uint256 _beforebalance = IERC20Metadata(_sellParams.fromToken).balanceOf(address(this));
uint256 _sellAmount = _sellParams.sellAmount;

// generate debt
_executeFromProxy(_payload);

// todo add error msg
// if (_sellAmount != OD.balanceOf(address(this)) - _beforebalance) revert();
emit log_named_uint('OPEN DOLLAR BALANCE', IERC20(_sellParams.fromToken).balanceOf(address(this)));

emit log_named_uint('RETH BAL POST-DEPOS', IERC20(_sellParams.toToken).balanceOf(address(this)));

// ISAFEEngine(safeManager.safeEngine()).safes();

// swap debt to collateral
_sellOnParaSwap(
Expand All @@ -162,16 +157,15 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
_sellAmount,
_minDstAmount
);
emit log_named_uint('RETH BAL POST SWAP', IERC20Metadata(_sellParams.toToken).balanceOf(address(this)));

uint256 _payBack = amount + premium;
IERC20(asset).approve(address(POOL), _payBack);
IERC20Metadata(asset).approve(address(POOL), _payBack);

return true;
}

function _executeFromProxy(bytes memory _payload) internal {
emit log_named_address('MSG SENDER', msg.sender);
// lock collateral on behalf of user and generate debt to address(this)
PS_ADAPTER_ODPROXY.execute(address(exitActions), _payload);
}

Expand All @@ -194,7 +188,7 @@ contract ParaswapSellAdapter is FlashLoanSimpleReceiverBase, IParaswapSellAdapte
if (_minDstAmount == 0) revert ZeroValue();

uint256 _initBalFromToken = _fromToken.balanceOf(address(this));
// if (_initBalFromToken < _sellAmount) revert InsufficientBalance();
if (_initBalFromToken < _sellAmount) revert InsufficientBalance();
uint256 _initBalToToken = _toToken.balanceOf(address(this));

address _tokenTransferProxy = augustus.getTokenTransferProxy();
Expand Down
1 change: 1 addition & 0 deletions src/leverage/interfaces/IParaswapSellAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ interface IParaswapSellAdapter {
*/
function requestFlashloan(
SellParams memory _sellParams,
uint256 _collateralLoan,
uint256 _minDstAmount,
uint256 _safeId,
bytes32 _cType
Expand Down
80 changes: 47 additions & 33 deletions test/e2e/E2ESwapExit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
pragma solidity 0.8.20;

import '@script/Registry.s.sol';
import {IERC20} from '@openzeppelin/token/ERC20/IERC20.sol';
import {IERC20Metadata} from '@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol';
import {IVault721} from '@opendollar/interfaces/proxies/IVault721.sol';
import {IDenominatedOracle} from '@opendollar/interfaces/oracles/IDenominatedOracle.sol';
import {DelayedOracleForTest} from '@opendollar/test/mocks/DelayedOracleForTest.sol';
import {AugustusRegistry} from '@aave-debt-swap/dependencies/paraswap/AugustusRegistry.sol';
import {ParaswapSellAdapter, IParaswapSellAdapter} from 'src/leverage/ParaswapSellAdapter.sol';
import {CommonTest} from 'test/e2e/common/CommonTest.t.sol';
import {Math} from '@opendollar/libraries/Math.sol';

contract E2ESwapExit is CommonTest {
using Math for uint256;

uint256 public constant PREMIUM = 500_000_000_000;
uint256 public constant INTEREST_RATE_MODE = 0;
uint16 public constant REF_CODE = 0;
Expand All @@ -21,17 +25,27 @@ contract E2ESwapExit is CommonTest {
IParaswapSellAdapter public sellAdapter;
IVault721.NFVState public userNFV;

IDenominatedOracle public rethOracle;
uint256 rethUsdPrice;

function setUp() public virtual override {
super.setUp();
rethOracle = IDenominatedOracle(MAINNET_DENOMINATED_RETH_USD_ORACLE);
(rethUsdPrice,) = rethOracle.getResultWithValidity();
setCTypePrice(RETH, rethUsdPrice);

userProxy = _deployOrFind(USER);
label(USER, 'USER-WALLET');
label(userProxy, 'USER-PROXY');
_openSafe(userProxy, RETH);

vm.prank(USER);
IERC20(RETH_ADDR).approve(userProxy, type(uint256).max);
IERC20Metadata(RETH_ADDR).approve(userProxy, type(uint256).max);

userNFV = vault721.getNfvState(vaults[userProxy]);

sellAdapter = new ParaswapSellAdapter(
address(systemCoin),
AugustusRegistry.ARBITRUM,
PARASWAP_AUGUSTUS_SWAPPER,
AAVE_POOL_ADDRESS_PROVIDER,
Expand All @@ -44,55 +58,55 @@ contract E2ESwapExit is CommonTest {
SELL_ADAPTER = address(sellAdapter);

sellAdapterProxy = _deployOrFind(SELL_ADAPTER);
label(SELL_ADAPTER, 'SELL-ADAPTER-CONTRACT');
label(sellAdapterProxy, 'SELL-ADAPTER-PROXY');

vm.prank(SELL_ADAPTER);
IERC20(RETH_ADDR).approve(sellAdapterProxy, type(uint256).max);
vm.startPrank(SELL_ADAPTER);
IERC20Metadata(RETH_ADDR).approve(sellAdapterProxy, type(uint256).max);
IERC20Metadata(OD_ADDR).approve(sellAdapterProxy, type(uint256).max);
vm.stopPrank();
}

/**
* todo: test with initial deposit of collateral
* initial deposit + loan to overcome the over-collateralization "loss"
* in being able to returning to loan in full + premium
*/
function testRequestFlashloan() public {
uint256 _sellAmount = 1400 ether;
assertEq(readCTypePrice(RETH), rethUsdPrice);
uint256 _collateralLoan = 1 ether;
uint256 _sellAmount = (_collateralLoan.wmul(rethUsdPrice) - PREMIUM) * 2 / 3;
emit log_named_uint('DEBT SELL AMOUNT', _sellAmount);

// from OD to RETH
(uint256 _dstAmount, IParaswapSellAdapter.SellParams memory _sellParams) =
_getFullUserInputWithAmount(OD_ADDR, RETH_ADDR, _sellAmount);

deal(RETH_ADDR, USER, PREMIUM);
// todo: lock initial capital in safe
uint256 _initCapital = _collateralLoan / 2;
deal(RETH_ADDR, USER, _initCapital);

vm.prank(userProxy);
safeManager.allowSAFE(vaults[userProxy], sellAdapterProxy, true);

vm.startPrank(USER);
IERC20(RETH_ADDR).approve(SELL_ADAPTER, PREMIUM);
sellAdapter.deposit(RETH_ADDR, PREMIUM);
IERC20Metadata(RETH_ADDR).approve(SELL_ADAPTER, _initCapital);
sellAdapter.deposit(RETH_ADDR, _initCapital);

assertEq(IERC20(RETH_ADDR).balanceOf(SELL_ADAPTER), PREMIUM);
assertEq(IERC20(OD_ADDR).balanceOf(SELL_ADAPTER), 0);
// assertEq(IERC20Metadata(RETH_ADDR).balanceOf(SELL_ADAPTER), _initCapital);
// assertEq(IERC20Metadata(OD_ADDR).balanceOf(SELL_ADAPTER), 0);

sellAdapter.requestFlashloan(_sellParams, _dstAmount, vaults[userProxy], RETH);
// assertEq(IERC20(RETH_ADDR).balanceOf(SELL_ADAPTER), 0);
sellAdapter.requestFlashloan(_sellParams, _collateralLoan, _dstAmount, vaults[userProxy], RETH);
// assertEq(IERC20Metadata(RETH_ADDR).balanceOf(SELL_ADAPTER), 0);

vm.stopPrank();
}

/**
* @dev lock collateral in USER safe from sellAdapterProxy
* and mint debt from USER safe to sellAdapterProxy
*/
// function testLockCollateralFromHandler() public {
// deal(RETH_ADDR, SELL_ADAPTER, SELL_AMOUNT);

// (uint256 _initCollateral,) = _getSAFE(RETH, userNFV.safeHandler);
// assertEq(_initCollateral, 0);

// vm.prank(SELL_ADAPTER);
// _lockCollateral(RETH, vaults[userProxy], SELL_AMOUNT, sellAdapterProxy);

// (uint256 _newCollateral,) = _getSAFE(RETH, userNFV.safeHandler);
// assertEq(_newCollateral, SELL_AMOUNT);
function setCTypePrice(bytes32 _cType, uint256 _price) public {
DelayedOracleForTest(address(delayedOracle[_cType])).setPriceAndValidity(_price, true);
oracleRelayer.updateCollateralPrice(_cType);
}

// assertEq(systemCoin.balanceOf(SELL_ADAPTER), 0);
// vm.prank(SELL_ADAPTER);
// _genDebtToAccount(SELL_ADAPTER, vaults[userProxy], SELL_AMOUNT * 2 / 3, sellAdapterProxy);
// assertEq(systemCoin.balanceOf(SELL_ADAPTER), SELL_AMOUNT * 2 / 3);
// }
function readCTypePrice(bytes32 _cType) public returns (uint256 _price) {
_price = delayedOracle[_cType].read();
}
}
1 change: 1 addition & 0 deletions test/e2e/E2ESwapSell.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ contract E2ESwapSell is BaseTest {
function setUp() public virtual {
vm.createSelectFork(vm.rpcUrl('mainnet'));
sellAdapter = new ParaswapSellAdapter(
address(0xaaa),
AugustusRegistry.ARBITRUM,
PARASWAP_AUGUSTUS_SWAPPER,
AAVE_POOL_ADDRESS_PROVIDER,
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -414,10 +414,10 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@opendollar/[email protected]d805d33e":
version "0.0.0-d805d33e"
resolved "https://registry.yarnpkg.com/@opendollar/contracts/-/contracts-0.0.0-d805d33e.tgz#ddab4b29d86478024a10a1e143419c96eba5c7e3"
integrity sha512-j/2DK1iziS4/ngHH4KyskBszHjXMp5fYhxNEyirxZYVSpJT4EMew6iaARYae5FCuLXgui7xxnvGp7lMtzgpt+g==
"@opendollar/[email protected]984c17c2":
version "0.0.0-984c17c2"
resolved "https://registry.yarnpkg.com/@opendollar/contracts/-/contracts-0.0.0-984c17c2.tgz#52a03256654cc7fe69cff430d60bed07d9a8648a"
integrity sha512-4AV3GKyi+deaSq1jsSUSP22/hkywYhF0EOE2v7cq7MnK3fuSKOSChKmLO0Hx/1PrRxhoE3/eIxqQuaPEqhpt0A==
dependencies:
"@defi-wonderland/solidity-utils" "0.0.0-4298c6c6"
"@openzeppelin/contracts" "4.9.6"
Expand Down

0 comments on commit bfa4ed4

Please sign in to comment.