diff --git a/script/DeployContract.s.sol b/script/DeployContract.s.sol index b40b2668..6a699872 100644 --- a/script/DeployContract.s.sol +++ b/script/DeployContract.s.sol @@ -52,7 +52,7 @@ contract DeployScript is Script { function setUp() public { vaultFactory = new VaultFactory(admin,WETH,admin); - controller = new Controller(address(vaultFactory), arbitrum_sequencer); + controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vm.prank(admin); vaultFactory.setController(address(controller)); diff --git a/script/DeployGoerli.s.sol b/script/DeployGoerli.s.sol index 1e435d2e..1e342cf9 100644 --- a/script/DeployGoerli.s.sol +++ b/script/DeployGoerli.s.sol @@ -54,7 +54,7 @@ contract DeployGoerliScript is Script { // setUp(); vaultFactory = new VaultFactory(admin,WETH,admin); - controller = new Controller(address(vaultFactory), arbitrum_sequencer); + controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vaultFactory.setController(address(controller)); diff --git a/src/Controller.sol b/src/Controller.sol index 82b8d574..528fbd2a 100644 --- a/src/Controller.sol +++ b/src/Controller.sol @@ -8,10 +8,12 @@ import "@chainlink/interfaces/AggregatorV3Interface.sol"; import "@chainlink/interfaces/AggregatorV2V3Interface.sol"; contract Controller { + address public immutable admin; VaultFactory public immutable vaultFactory; AggregatorV2V3Interface internal sequencerUptimeFeed; uint256 private constant GRACE_PERIOD_TIME = 3600; + uint256 public constant VAULTS_LENGTH = 2; /*////////////////////////////////////////////////////////////// ERRORS @@ -21,15 +23,16 @@ contract Controller { error SequencerDown(); error GracePeriodNotOver(); error ZeroAddress(); - error EpochFinishedAlready(); + error NotZeroTVL(); error PriceNotAtStrikePrice(int256 price); error EpochNotStarted(); error EpochExpired(); error OraclePriceZero(); error RoundIDOutdated(); + error TimestampZero(); + error AddressNotAdmin(); error EpochNotExist(); error EpochNotExpired(); - error VaultNotZeroTVL(); /*////////////////////////////////////////////////////////////// EVENTS @@ -52,12 +55,59 @@ contract Controller { int256 depegPrice ); + /* solhint-disable var-name-mixedcase */ struct VaultTVL { uint256 RISK_claimTVL; uint256 RISK_finalTVL; uint256 INSR_claimTVL; uint256 INSR_finalTVL; } + /* solhint-enable var-name-mixedcase */ + + /*////////////////////////////////////////////////////////////// + MODIFIERS + //////////////////////////////////////////////////////////////*/ + + /** @notice Only admin addresses can call functions that use this modifier + */ + modifier onlyAdmin() { + if(msg.sender != admin) + revert AddressNotAdmin(); + _; + } + + /** @notice Modifier to ensure market exists, current market epoch time and price are valid + * @param marketIndex Target market index + * @param epochEnd End of epoch set for market + */ + modifier isDisaster(uint256 marketIndex, uint256 epochEnd) { + address[] memory vaultsAddress = vaultFactory.getVaults(marketIndex); + if( + vaultsAddress.length != VAULTS_LENGTH + ) + revert MarketDoesNotExist(marketIndex); + + address vaultAddress = vaultsAddress[0]; + Vault vault = Vault(vaultAddress); + + if(vault.idExists(epochEnd) == false) + revert EpochNotExist(); + + if( + vault.strikePrice() < getLatestPrice(vault.tokenInsured()) + ) + revert PriceNotAtStrikePrice(getLatestPrice(vault.tokenInsured())); + + if( + vault.idEpochBegin(epochEnd) > block.timestamp) + revert EpochNotStarted(); + + if( + block.timestamp > epochEnd + ) + revert EpochExpired(); + _; + } /*////////////////////////////////////////////////////////////// CONSTRUCTOR @@ -65,18 +115,24 @@ contract Controller { /** @notice Contract constructor * @param _factory VaultFactory address + * @param _admin Admin address * @param _l2Sequencer Arbitrum sequencer address */ constructor( address _factory, + address _admin, address _l2Sequencer ) { + if(_admin == address(0)) + revert ZeroAddress(); + if(_factory == address(0)) revert ZeroAddress(); if(_l2Sequencer == address(0)) revert ZeroAddress(); + admin = _admin; vaultFactory = VaultFactory(_factory); sequencerUptimeFeed = AggregatorV2V3Interface(_l2Sequencer); } @@ -91,38 +147,17 @@ contract Controller { */ function triggerDepeg(uint256 marketIndex, uint256 epochEnd) public + isDisaster(marketIndex, epochEnd) { address[] memory vaultsAddress = vaultFactory.getVaults(marketIndex); Vault insrVault = Vault(vaultsAddress[0]); Vault riskVault = Vault(vaultsAddress[1]); - if( - vaultsAddress[0] == address(0) || vaultsAddress[1] == address(0) - ) - revert MarketDoesNotExist(marketIndex); - - if(insrVault.idExists(epochEnd) == false) - revert EpochNotExist(); - - if( - insrVault.strikePrice() <= getLatestPrice(insrVault.tokenInsured()) - ) - revert PriceNotAtStrikePrice(getLatestPrice(insrVault.tokenInsured())); - - if( - insrVault.idEpochBegin(epochEnd) > block.timestamp) - revert EpochNotStarted(); - - if( - block.timestamp > epochEnd - ) - revert EpochExpired(); - //require this function cannot be called twice in the same epoch for the same vault - if(insrVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); - if(riskVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); + if(insrVault.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); + if(riskVault.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); insrVault.endEpoch(epochEnd); riskVault.endEpoch(epochEnd); @@ -140,17 +175,6 @@ contract Controller { insrVault.idFinalTVL(epochEnd) ); - AggregatorV3Interface priceFeed = AggregatorV3Interface( - vaultFactory.tokenToOracle(insrVault.tokenInsured()) - ); - ( - , - int256 price, - , - , - - ) = priceFeed.latestRoundData(); - emit DepegInsurance( keccak256( abi.encodePacked( @@ -163,7 +187,7 @@ contract Controller { true, epochEnd, block.timestamp, - price + getLatestPrice(insrVault.tokenInsured()) ); } @@ -172,6 +196,9 @@ contract Controller { * @param epochEnd End of epoch set for market */ function triggerEndEpoch(uint256 marketIndex, uint256 epochEnd) public { + if( + vaultFactory.getVaults(marketIndex).length != VAULTS_LENGTH) + revert MarketDoesNotExist(marketIndex); if( block.timestamp <= epochEnd) revert EpochNotExpired(); @@ -180,20 +207,15 @@ contract Controller { Vault insrVault = Vault(vaultsAddress[0]); Vault riskVault = Vault(vaultsAddress[1]); - - if( - vaultsAddress[0] == address(0) || vaultsAddress[1] == address(0) - ) - revert MarketDoesNotExist(marketIndex); if(insrVault.idExists(epochEnd) == false || riskVault.idExists(epochEnd) == false) revert EpochNotExist(); //require this function cannot be called twice in the same epoch for the same vault - if(insrVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); - if(riskVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); + if(insrVault.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); + if(riskVault.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); insrVault.endEpoch(epochEnd); riskVault.endEpoch(epochEnd); @@ -224,57 +246,10 @@ contract Controller { getLatestPrice(insrVault.tokenInsured()) ); } - /** @notice Trigger epoch invalid when one vault has 0 TVL - * @param marketIndex Target market index - * @param epochEnd End of epoch set for market - */ - function triggerNullEpoch(uint256 marketIndex, uint256 epochEnd) public { - address[] memory vaultsAddress = vaultFactory.getVaults(marketIndex); - - Vault insrVault = Vault(vaultsAddress[0]); - Vault riskVault = Vault(vaultsAddress[1]); - if( - vaultsAddress[0] == address(0) || vaultsAddress[1] == address(0) - ) - revert MarketDoesNotExist(marketIndex); - - if(insrVault.idExists(epochEnd) == false || riskVault.idExists(epochEnd) == false) - revert EpochNotExist(); - - if(block.timestamp < insrVault.idEpochBegin(epochEnd)) - revert EpochNotStarted(); - - if(insrVault.idExists(epochEnd) == false || riskVault.idExists(epochEnd) == false) - revert EpochNotExist(); - - //require this function cannot be called twice in the same epoch for the same vault - if(insrVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); - if(riskVault.idEpochEnded(epochEnd)) - revert EpochFinishedAlready(); - - //set claim TVL to 0 if total assets are 0 - if(insrVault.totalAssets(epochEnd) == 0){ - insrVault.endEpoch(epochEnd); - riskVault.endEpoch(epochEnd); - - insrVault.setClaimTVL(epochEnd, 0); - riskVault.setClaimTVL(epochEnd, riskVault.idFinalTVL(epochEnd)); - - riskVault.setEpochNull(epochEnd); - } - else if(riskVault.totalAssets(epochEnd) == 0){ - insrVault.endEpoch(epochEnd); - riskVault.endEpoch(epochEnd); - - insrVault.setClaimTVL(epochEnd, insrVault.idFinalTVL(epochEnd) ); - riskVault.setClaimTVL(epochEnd, 0); - - insrVault.setEpochNull(epochEnd); - } - else revert VaultNotZeroTVL(); - } + /*////////////////////////////////////////////////////////////// + ADMIN SETTINGS + //////////////////////////////////////////////////////////////*/ /*////////////////////////////////////////////////////////////// GETTERS @@ -317,7 +292,7 @@ contract Controller { uint80 roundID, int256 price, , - , + uint256 timeStamp, uint80 answeredInRound ) = priceFeed.latestRoundData(); @@ -336,6 +311,9 @@ contract Controller { if(answeredInRound < roundID) revert RoundIDOutdated(); + if(timeStamp == 0) + revert TimestampZero(); + return price; } diff --git a/src/SemiFungibleVault.sol b/src/SemiFungibleVault.sol index ff373d6d..24bbe962 100644 --- a/src/SemiFungibleVault.sol +++ b/src/SemiFungibleVault.sol @@ -30,12 +30,14 @@ abstract contract SemiFungibleVault is ERC1155Supply { * @param owner receiver who will own of the tokens representing this deposit * @param id Vault id * @param assets Amount of owner assets to deposit into vault + * @param shares Amount of shares to mint for owner */ event Deposit( address caller, address indexed owner, uint256 indexed id, - uint256 assets + uint256 assets, + uint256 shares ); /** @notice Withdraw from vault when event is emitted @@ -90,7 +92,7 @@ abstract contract SemiFungibleVault is ERC1155Supply { _mint(receiver, id, assets, EMPTY); - emit Deposit(msg.sender, receiver, id, assets); + emit Deposit(msg.sender, receiver, id, assets, assets); } /** @notice Triggers withdraw from vault and burns receivers' shares @@ -126,9 +128,7 @@ abstract contract SemiFungibleVault is ERC1155Supply { /**@notice Returns total assets for token * @param _id uint256 token id of token */ - function totalAssets(uint256 _id) public view virtual returns (uint256){ - return totalSupply(_id); - } + function totalAssets(uint256 _id) public view virtual returns (uint256); /** @notice Shows assets conversion output from withdrawing assets diff --git a/src/Vault.sol b/src/Vault.sol index eccb583d..58e7e57d 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -26,8 +26,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { error OwnerDidNotAuthorize(address _sender, address _owner); error EpochEndMustBeAfterBegin(); error MarketEpochExists(); - error TimeLocked(); - error FeeCannotBe0(); /*/////////////////////////////////////////////////////////////// IMMUTABLES AND STORAGE @@ -42,9 +40,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { uint256[] public epochs; uint256 public timewindow; - uint256 public immutable timeLock = 7 days; - uint256 public lastLocked; - /*////////////////////////////////////////////////////////////// MAPPINGS //////////////////////////////////////////////////////////////*/ @@ -58,7 +53,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { // @audit id can be uint32 mapping(uint256 => bool) public idExists; mapping(uint256 => uint256) public epochFee; - mapping(uint256 => bool) public epochNull; /*////////////////////////////////////////////////////////////// MODIFIERS @@ -72,13 +66,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { _; } - modifier timelocker(){ - if(block.timestamp < timeLock + lastLocked) - revert TimeLocked(); - - _; - } - /** @notice Only controller addresses can call functions that use this modifier */ modifier onlyController() { @@ -149,7 +136,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { factory = msg.sender; controller = _controller; timewindow = 1; - lastLocked = block.timestamp; } /*/////////////////////////////////////////////////////////////// @@ -174,11 +160,11 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { nonReentrant { - assert(asset.transferFrom(msg.sender, address(this), assets)); + asset.transferFrom(msg.sender, address(this), assets); _mint(receiver, id, assets, EMPTY); - emit Deposit(msg.sender, receiver, id, assets); + emit Deposit(msg.sender, receiver, id, assets, assets); } /** @@ -198,11 +184,11 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { IWETH(address(asset)).deposit{value: msg.value}(); _mint(receiver, id, msg.value, EMPTY); - emit Deposit(msg.sender, receiver, id, msg.value); + emit Deposit(msg.sender, receiver, id, msg.value, msg.value); } /** - @notice Withdraw entitled deposited assets, checking if a depeg event + @notice Withdraw entitled deposited assets, checking if a depeg event //TODO add GOV token rewards @param id uint256 in UNIX timestamp, representing the end date of the epoch. Example: Epoch ends in 30th June 2022 at 00h 00min 00sec: 1654038000; @param assets uint256 of how many assets you want to withdraw, this value will be used to calculate how many assets you are entitle to according to the events; @param receiver Address of the receiver of the assets provided by this function, that represent the ownership of the transfered asset; @@ -226,24 +212,16 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { isApprovedForAll(owner, msg.sender) == false) revert OwnerDidNotAuthorize(msg.sender, owner); - uint256 entitledShares; + uint256 entitledShares = previewWithdraw(id, assets); _burn(owner, id, assets); - if(epochNull[id] == false) { - //Taking fee from the amount - entitledShares = previewWithdraw(id, assets); - uint256 feeValue = calculateWithdrawalFeeValue(entitledShares, id); - entitledShares = entitledShares - feeValue; - assert(asset.transfer(treasury, feeValue)); - } - else{ - entitledShares = assets; - } - if (entitledShares > 0) { - assert(asset.transfer(receiver, entitledShares)); - } + //Taking fee from the amount + uint256 feeValue = calculateWithdrawalFeeValue(entitledShares, id); + entitledShares = entitledShares - feeValue; + asset.transfer(treasury, feeValue); emit Withdraw(msg.sender, receiver, owner, id, assets, entitledShares); + asset.transfer(receiver, entitledShares); return entitledShares; } @@ -289,7 +267,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { @notice Factory function, changes treasury address @param _treasury New treasury address */ - function changeTreasury(address _treasury) public onlyFactory timelocker { + function changeTreasury(address _treasury) public onlyFactory { if(_treasury == address(0)) revert AddressZero(); treasury = _treasury; @@ -299,7 +277,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { @notice Factory function, changes vault time window @param _timewindow New vault time window */ - function changeTimewindow(uint256 _timewindow) public onlyFactory timelocker{ + function changeTimewindow(uint256 _timewindow) public onlyFactory { timewindow = _timewindow; } @@ -307,7 +285,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { @notice Factory function, changes controller address @param _controller New controller address */ - function changeController(address _controller) public onlyFactory timelocker{ + function changeController(address _controller) public onlyFactory { if(_controller == address(0)) revert AddressZero(); controller = _controller; @@ -326,9 +304,6 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { if(_withdrawalFee > 150) revert FeeMoreThan150(_withdrawalFee); - if(_withdrawalFee == 0) - revert FeeCannotBe0(); - if(idExists[epochEnd] == true) revert MarketEpochExists(); @@ -364,7 +339,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { @param id uint256 in UNIX timestamp, representing the end date of the epoch. Example: Epoch ends in 30th June 2022 at 00h 00min 00sec: 1654038000 @param claimTVL uint256 representing the TVL the counterparty vault has, storing this value in a mapping */ - function setClaimTVL(uint256 id, uint256 claimTVL) public onlyController marketExists(id) { + function setClaimTVL(uint256 id, uint256 claimTVL) public onlyController { idClaimTVL[id] = claimTVL; } @@ -379,11 +354,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { onlyController marketExists(id) { - assert(asset.transfer(_counterparty, idFinalTVL[id])); - } - - function setEpochNull(uint256 id) public onlyController { - epochNull[id] = true; + asset.transfer(_counterparty, idFinalTVL[id]); } /*/////////////////////////////////////////////////////////////// diff --git a/src/VaultFactory.sol b/src/VaultFactory.sol index c25e9dd8..bfd70f17 100644 --- a/src/VaultFactory.sol +++ b/src/VaultFactory.sol @@ -184,15 +184,14 @@ contract VaultFactory { address _oracle, string memory _name ) public onlyAdmin returns (address insr, address rsk) { - - if(controller == address(0)) - revert ControllerNotSet(); - if( IController(controller).getVaultFactory() != address(this) ) revert AddressFactoryNotInController(); + if(controller == address(0)) + revert ControllerNotSet(); + marketIndex += 1; //y2kUSDC_99*RISK or y2kUSDC_99*HEDGE @@ -310,9 +309,6 @@ contract VaultFactory { public onlyAdmin { - if(_treasury == address(0)) - revert AddressZero(); - treasury = _treasury; address[] memory vaults = indexVaults[_marketIndex]; Vault insr = Vault(vaults[0]); diff --git a/src/rewards/RewardsFactory.sol b/src/rewards/RewardsFactory.sol index d7209c24..8bee8bd6 100644 --- a/src/rewards/RewardsFactory.sol +++ b/src/rewards/RewardsFactory.sol @@ -18,19 +18,29 @@ contract RewardsFactory { error AddressNotAdmin(); error EpochDoesNotExist(); + /*////////////////////////////////////////////////////////////// + MAPPINGS + //////////////////////////////////////////////////////////////*/ + + //mapping(uint => mapping(uint => address[])) public marketIndex_epoch_StakingRewards; //Market Index, Epoch, Staking Rewards [0] = insrance, [1] = risk + // solhint-disable-next-line var-name-mixedcase + mapping(bytes32 => address[]) public hashedIndex_StakingRewards; //Hashed Index, Staking Rewards [0] = insrance, [1] = risk + /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** @notice Creates staking rewards when event is emitted - * @param marketIndex Current market epoch ID - * @param epochdEndId Epoch Id of market - * @param addressFarms farms addresss [0] hedge [1] risk + * @param marketEpochId Current market epoch ID + * @param mIndex Current market index + * @param hedgeFarm Hedge farm address + * @param riskFarm Risk farm address */ event CreatedStakingReward( - uint indexed marketIndex, - uint256 indexed epochdEndId, - address[2] indexed addressFarms + bytes32 indexed marketEpochId, + uint256 indexed mIndex, + address hedgeFarm, + address riskFarm ); /*////////////////////////////////////////////////////////////// @@ -105,15 +115,38 @@ contract RewardsFactory { _rewardRate ); - address[2] memory Farms; - Farms = [address(insrStake),address(riskStake)]; + bytes32 hashedIndex = keccak256(abi.encode(_marketIndex, _epochEnd)); + hashedIndex_StakingRewards[hashedIndex] = [ + address(insrStake), + address(riskStake) + ]; emit CreatedStakingReward( + keccak256( + abi.encodePacked( + _marketIndex, + Vault(_insrToken).idEpochBegin(_epochEnd), + _epochEnd + ) + ), _marketIndex, - _epochEnd, - Farms + address(insrStake), + address(riskStake) ); return (address(insrStake), address(riskStake)); } + + /** @notice Lookup hashed indexes + * @param _index Target index + * @param _epoch Target epoch + * @return hashedIndex hashed index + */ + function getHashedIndex(uint256 _index, uint256 _epoch) + public + pure + returns (bytes32 hashedIndex) + { + return keccak256(abi.encode(_index, _epoch)); + } } diff --git a/test/AssertTest.t.sol b/test/AssertTest.t.sol index c5b473e9..429a1f41 100644 --- a/test/AssertTest.t.sol +++ b/test/AssertTest.t.sol @@ -16,7 +16,7 @@ import {IWETH} from "./interfaces/IWETH.sol"; contract AssertTest is Helper { - + /*/////////////////////////////////////////////////////////////// CREATION functions //////////////////////////////////////////////////////////////*/ @@ -293,8 +293,9 @@ contract AssertTest is Helper { function testCreateController() public { vm.startPrank(admin); - Controller test_controller = new Controller(address(vaultFactory), arbitrum_sequencer); + Controller test_controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); assertEq(address(vaultFactory), address(test_controller.vaultFactory())); + assertEq(admin, test_controller.admin()); vm.stopPrank(); } @@ -304,7 +305,7 @@ contract AssertTest is Helper { DepositDepeg(); vm.startPrank(admin); DepegOracle depegOracle = new DepegOracle(address(oracleFRAX), address(admin)); - Controller controller = new Controller(address(vaultFactory), arbitrum_sequencer); + Controller controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, address(depegOracle), "y2kFRAX_99*"); vaultFactory.setController(address(controller)); vm.stopPrank(); @@ -319,7 +320,7 @@ contract AssertTest is Helper { function testTriggerEndEpoch() public { DepositDepeg(); vm.startPrank(admin); - Controller test_controller = new Controller(address(vaultFactory), arbitrum_sequencer); + Controller test_controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vaultFactory.setController(address(test_controller)); vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); vm.warp(endEpoch + 1 days); @@ -329,72 +330,6 @@ contract AssertTest is Helper { vm.stopPrank(); } - function testNullEpochHedge() public { - - vm.startPrank(admin); - vm.deal(degen, AMOUNT); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - - address hedge = vaultFactory.getVaults(1)[0]; - address risk = vaultFactory.getVaults(1)[1]; - Vault vHedge = Vault(hedge); - Vault vRisk = Vault(risk); - - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(degen)); - vm.startPrank(degen); - vHedge.depositETH{value: AMOUNT}(endEpoch, degen); - vm.stopPrank(); - - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(hedge)); - - vm.warp(vHedge.idEpochBegin(endEpoch)); - controller.triggerNullEpoch(vaultFactory.marketIndex(), endEpoch); - assertTrue(vHedge.idClaimTVL(endEpoch) == AMOUNT && vRisk.idClaimTVL(endEpoch) == 0, "Claim TVL not zero"); - assertTrue(vHedge.idFinalTVL(endEpoch) == AMOUNT && vRisk.idFinalTVL(endEpoch) == 0, "Final TVL not zero"); - assertTrue(vHedge.totalAssets(endEpoch) == AMOUNT && vRisk.totalAssets(endEpoch) == 0, "Total TVL not zero"); - - vm.startPrank(degen); - vHedge.withdraw(endEpoch, AMOUNT, degen, degen); - vm.stopPrank(); - - assertTrue(ERC20(WETH).balanceOf(degen) == AMOUNT, "WETH not returned"); - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(degen)); - } - - function testNullEpochRisk() public { - - vm.startPrank(admin); - vm.deal(degen, AMOUNT); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - - address hedge = vaultFactory.getVaults(1)[0]; - address risk = vaultFactory.getVaults(1)[1]; - Vault vHedge = Vault(hedge); - Vault vRisk = Vault(risk); - - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(degen)); - vm.startPrank(degen); - vRisk.depositETH{value: AMOUNT}(endEpoch, degen); - vm.stopPrank(); - - vm.warp(vRisk.idEpochBegin(endEpoch)); - controller.triggerNullEpoch(vaultFactory.marketIndex(), endEpoch); - assertTrue(vRisk.idClaimTVL(endEpoch) == AMOUNT && vHedge.idClaimTVL(endEpoch) == 0, "Claim TVL not zero"); - assertTrue(vRisk.idFinalTVL(endEpoch) == AMOUNT && vHedge.idFinalTVL(endEpoch) == 0, "Final TVL not zero"); - assertTrue(vRisk.totalAssets(endEpoch) == AMOUNT && vHedge.totalAssets(endEpoch) == 0, "Total TVL not zero"); - - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(risk)); - - vm.startPrank(degen); - vRisk.withdraw(endEpoch, AMOUNT, degen, degen); - vm.stopPrank(); - - assertTrue(ERC20(WETH).balanceOf(degen) == AMOUNT, "WETH not returned"); - emit log_named_uint("WETH balance", ERC20(WETH).balanceOf(degen)); - } - /*/////////////////////////////////////////////////////////////// WITHDRAW functions //////////////////////////////////////////////////////////////*/ @@ -473,36 +408,21 @@ contract AssertTest is Helper { vm.stopPrank(); } - /*/////////////////////////////////////////////////////////////// - VAULTFACTORY TIMELOCK functions - //////////////////////////////////////////////////////////////*/ - function testTimelocks() public { - vm.startPrank(admin); - - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.warp(block.timestamp + 7 days + 1); - vaultFactory.changeTimewindow(1,5 days); - vm.warp(block.timestamp + 7 days + 1); - vaultFactory.changeTimewindow(1,5); - - vm.stopPrank(); - } - /*/////////////////////////////////////////////////////////////// GOVTOKEN functions //////////////////////////////////////////////////////////////*/ - // function testMintGovToken() public { - // vm.startPrank(admin); - // vaultFactory.createNewMarket(NULL_BALANCE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kSTETH_99*"); - // rewardsFactory.createStakingRewards(SINGLE_MARKET_INDEX, endEpoch, REWARDS_DURATION, REWARD_RATE); - // govToken.moneyPrinterGoesBrr(alice); - // uint256 aliceBalance = ERC20(address(govToken)).balanceOf(alice); - // emit log_named_int("Alice Balance", int256(aliceBalance)); - // assert(aliceBalance != NULL_BALANCE); - // vm.stopPrank(); - // } + function testMintGovToken() public { + vm.startPrank(admin); + vaultFactory.createNewMarket(NULL_BALANCE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kSTETH_99*"); + rewardsFactory.createStakingRewards(SINGLE_MARKET_INDEX, endEpoch, REWARDS_DURATION, REWARD_RATE); + govToken.moneyPrinterGoesBrr(alice); + uint256 aliceBalance = ERC20(address(govToken)).balanceOf(alice); + emit log_named_int("Alice Balance", int256(aliceBalance)); + assert(aliceBalance != NULL_BALANCE); + vm.stopPrank(); + } /*/////////////////////////////////////////////////////////////// REWARDSFACTORY functions @@ -559,6 +479,14 @@ contract AssertTest is Helper { } + function testGetHashedIndex() public{ + vm.startPrank(admin); + bytes32 hashedIndex = rewardsFactory.getHashedIndex(SINGLE_MARKET_INDEX, beginEpoch); + assertEq(hashedIndex, keccak256(abi.encode(SINGLE_MARKET_INDEX, beginEpoch))); + vm.stopPrank(); + } + + /*////////////////////////////////////////////////////////////// PEGORACLE functions //////////////////////////////////////////////////////////////*/ @@ -634,7 +562,6 @@ contract AssertTest is Helper { vm.startPrank(bob); vHedge.withdraw(endEpoch, 10 ether, bob, alice); - assertTrue(vHedge.balanceOf(alice,endEpoch) == 0); vm.stopPrank(); } diff --git a/test/FuzzHelper.sol b/test/FuzzHelper.sol index 4da3fcda..c6853db0 100644 --- a/test/FuzzHelper.sol +++ b/test/FuzzHelper.sol @@ -69,7 +69,7 @@ contract FuzzHelper is Test { function setUp() public { vaultFactory = new VaultFactory(admin,WETH,admin); - controller = new Controller(address(vaultFactory), arbitrum_sequencer); + controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vm.prank(admin); vaultFactory.setController(address(controller)); diff --git a/test/FuzzTest.t.sol b/test/FuzzTest.t.sol index 78546a6a..c854a048 100644 --- a/test/FuzzTest.t.sol +++ b/test/FuzzTest.t.sol @@ -60,6 +60,14 @@ contract FuzzTest is FuzzHelper{ vm.stopPrank(); } + function testFuzzGetHashedIndex(uint256 index) public{ + vm.startPrank(admin); + vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + bytes32 hashedIndex = rewardsFactory.getHashedIndex(index, beginEpoch); + assertEq(hashedIndex, keccak256(abi.encode(index, beginEpoch))); + vm.stopPrank(); + } + /*function testFuzzControllerDepeg(uint256 index) public{ vm.assume(index >= SINGLE_MARKET_INDEX && index <= ALL_MARKETS_INDEX); vm.startPrank(admin); diff --git a/test/Helper.sol b/test/Helper.sol index b5dce357..c4eb7af8 100644 --- a/test/Helper.sol +++ b/test/Helper.sol @@ -78,7 +78,7 @@ contract Helper is Test { function setUp() public { vaultFactory = new VaultFactory(admin,WETH,admin); - controller = new Controller(address(vaultFactory), arbitrum_sequencer); + controller = new Controller(address(vaultFactory),admin, arbitrum_sequencer); vm.prank(admin); vaultFactory.setController(address(controller)); @@ -109,11 +109,13 @@ contract Helper is Test { //ALICE hedge DEPOSIT vm.startPrank(alice); + ERC20(WETH).approve(hedge, AMOUNT); vHedge.depositETH{value: AMOUNT}(endEpoch, alice); vm.stopPrank(); //BOB hedge DEPOSIT vm.startPrank(bob); + ERC20(WETH).approve(hedge, AMOUNT * BOB_MULTIPLIER); vHedge.depositETH{value: AMOUNT * BOB_MULTIPLIER}(endEpoch, bob); assertTrue(vHedge.balanceOf(bob,endEpoch) == 20 ether); @@ -124,6 +126,7 @@ contract Helper is Test { //CHAD risk DEPOSIT vm.startPrank(chad); + ERC20(WETH).approve(risk, AMOUNT * CHAD_MULTIPLIER); vRisk.depositETH{value: AMOUNT * CHAD_MULTIPLIER}(endEpoch, chad); assertTrue(vRisk.balanceOf(chad,endEpoch) == (AMOUNT * CHAD_MULTIPLIER)); @@ -131,6 +134,7 @@ contract Helper is Test { //DEGEN risk DEPOSIT vm.startPrank(degen); + ERC20(WETH).approve(risk, AMOUNT * DEGEN_MULTIPLIER); vRisk.depositETH{value: AMOUNT * DEGEN_MULTIPLIER}(endEpoch, degen); assertTrue(vRisk.balanceOf(degen,endEpoch) == (AMOUNT * DEGEN_MULTIPLIER)); @@ -159,6 +163,7 @@ contract Helper is Test { //ALICE hedge DEPOSIT vm.startPrank(alice); + ERC20(WETH).approve(hedge, AMOUNT); vHedge.depositETH{value: AMOUNT}(endEpoch, alice); assertTrue(vHedge.balanceOf(alice,endEpoch) == (AMOUNT)); @@ -166,6 +171,7 @@ contract Helper is Test { //BOB hedge DEPOSIT vm.startPrank(bob); + ERC20(WETH).approve(hedge, AMOUNT * BOB_MULTIPLIER); vHedge.depositETH{value: AMOUNT * BOB_MULTIPLIER}(endEpoch, bob); assertTrue(vHedge.balanceOf(bob,endEpoch) == (AMOUNT * BOB_MULTIPLIER)); @@ -173,6 +179,7 @@ contract Helper is Test { //CHAD risk DEPOSIT vm.startPrank(chad); + ERC20(WETH).approve(risk, AMOUNT * CHAD_MULTIPLIER); vRisk.depositETH{value: AMOUNT * CHAD_MULTIPLIER}(endEpoch, chad); assertTrue(vRisk.balanceOf(chad,endEpoch) == (AMOUNT * CHAD_MULTIPLIER)); @@ -180,6 +187,7 @@ contract Helper is Test { //DEGEN risk DEPOSIT vm.startPrank(degen); + ERC20(WETH).approve(risk, AMOUNT * DEGEN_MULTIPLIER); vRisk.depositETH{value: AMOUNT * DEGEN_MULTIPLIER}(endEpoch, degen); assertTrue(vRisk.balanceOf(degen,endEpoch) == (AMOUNT * DEGEN_MULTIPLIER)); @@ -217,6 +225,8 @@ contract Helper is Test { Vault vHedge = Vault(hedge); Vault vRisk = Vault(risk); + vm.warp(endEpoch + 1 days); + emit log_named_int("strike price", vHedge.strikePrice()); emit log_named_int("oracle price", controller.getLatestPrice(_token)); diff --git a/test/RevertTest.t.sol b/test/RevertTest.t.sol index 77529121..f192c9d3 100644 --- a/test/RevertTest.t.sol +++ b/test/RevertTest.t.sol @@ -22,7 +22,7 @@ contract RevertTest is Helper { function testSequencerDown() public { //create invalid controller(w/any address other than arbitrum_sequencer) - controller = new Controller(address(vaultFactory), oracleFEI); + controller = new Controller(address(vaultFactory),admin, oracleFEI); //create fake oracle for price feed vm.startPrank(admin); @@ -37,76 +37,55 @@ contract RevertTest is Helper { vm.stopPrank(); } - function testFailControllerMarketDoesNotExist() public { + function testControllerMarketDoesNotExist() public { //create fake oracle for price feed - DepositDepeg(); + vm.startPrank(admin); + //FakeOracle fakeOracle = new FakeOracle(oracleFRAX, STRIKE_PRICE_FAKE_ORACLE); + vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + vm.stopPrank(); //expect MarketDoesNotExist + vm.startPrank(admin); emit log_named_uint("Number of markets", vaultFactory.marketIndex()); - vm.warp(endEpoch - 1 days); - //vm.expectRevert(abi.encodeWithSelector(Controller.MarketDoesNotExist.selector, MARKET_OVERFLOW)); - controller.triggerDepeg(69, endEpoch); + vm.expectRevert(abi.encodeWithSelector(Controller.MarketDoesNotExist.selector, MARKET_OVERFLOW)); + controller.triggerEndEpoch(MARKET_OVERFLOW, endEpoch); + vm.stopPrank(); } - function testFailControllerDoubleTrigger() public { - //create fake oracle for price feed + function testControllerZeroAddress() public { + + + //expect ZeroAddress for admin vm.startPrank(admin); - //FakeOracle fakeOracle = new FakeOracle(oracleFRAX, STRIKE_PRICE_FAKE_ORACLE); vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + vm.expectRevert(Controller.ZeroAddress.selector); + Controller controller = new Controller(address(0), address(vaultFactory), arbitrum_sequencer); vm.stopPrank(); - Deposit(1); - vm.warp(endEpoch + 1); - controller.triggerEndEpoch(1, endEpoch); - - controller.triggerEndEpoch(1, endEpoch); - } + //expect ZeroAddress for vaultFactory + vm.startPrank(admin); + vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + vm.expectRevert(Controller.ZeroAddress.selector); + controller = new Controller(address(admin), address(0), arbitrum_sequencer); + vm.stopPrank(); - function testFailControllerDoubleTrigger2() public { - DepositDepeg(); - vm.warp(beginEpoch + 5); - ControllerDepeg(tokenFRAX, 1); - //vm.expectRevert(Controller.EpochFinishedAlready.selector); - controller.triggerNullEpoch(1, endEpoch); + //expected ZeroAddress for arbitrum_sequencer + vm.startPrank(admin); + vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + vm.expectRevert(Controller.ZeroAddress.selector); + controller = new Controller(address(admin), address(vaultFactory), address(0)); + vm.stopPrank(); } - // function testControllerZeroAddress() public { - - - // //expect ZeroAddress for admin - // vm.startPrank(admin); - // vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - // vm.expectRevert(Controller.ZeroAddress.selector); - // Controller controller = new Controller(address(0), arbitrum_sequencer); - // vm.stopPrank(); - - // //expect ZeroAddress for vaultFactory - // vm.startPrank(admin); - // vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - // vm.expectRevert(Controller.ZeroAddress.selector); - // controller = new Controller(address(admin), arbitrum_sequencer); - // vm.stopPrank(); - - // //expected ZeroAddress for arbitrum_sequencer - // vm.startPrank(admin); - // vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - // vm.expectRevert(Controller.ZeroAddress.selector); - // controller = new Controller(address(admin), address(0)); - // vm.stopPrank(); - // } - function testFailControllerEpochNotExpired() public { vm.startPrank(admin); vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); + controller.triggerEndEpoch(vaultFactory.marketIndex(), endEpoch); vm.stopPrank(); - //vm.expectRevert(Controller.EpochNotExpired.selector); - //controller.triggerEndEpoch(vaultFactory.marketIndex(), endEpoch); - - vm.warp(endEpoch - 1); - - //vm.expectRevert(Controller.EpochNotExpired.selector); + vm.startPrank(admin); controller.triggerEndEpoch(vaultFactory.marketIndex(), endEpoch); + vm.stopPrank(); } @@ -114,15 +93,15 @@ contract RevertTest is Helper { //testing triggerEndEpoch vm.startPrank(admin); vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - //vm.expectRevert(Controller.EpochNotExist.selector); - controller.triggerEndEpoch(vaultFactory.marketIndex(), 2); - + controller.triggerEndEpoch(vaultFactory.marketIndex(), 0); + vm.stopPrank(); //testing isDisaster + vm.startPrank(admin); //vm.expectRevert(Controller.EpochNotExist.selector); //controller.triggerDepeg(vaultFactory.marketIndex(), block.timestamp); + vm.stopPrank(); } @@ -185,71 +164,6 @@ contract RevertTest is Helper { vm.stopPrank(); } - function testFailNullEpochRevEPOCHNOTSTARTED() public { - //need to fix triggerNullEpoch - vm.startPrank(admin); - vm.deal(alice, DEGEN_MULTIPLIER * AMOUNT); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - - address hedge = vaultFactory.getVaults(1)[0]; - Vault vHedge = Vault(hedge); - address risk = vaultFactory.getVaults(1)[1]; - Vault vRisk = Vault(risk); - - vm.startPrank(alice); - vHedge.depositETH{value: AMOUNT}(endEpoch, alice); - //vRisk.depositETH{value: AMOUNT}(endEpoch, alice); - vm.stopPrank(); - - vm.startPrank(admin); - vm.warp(beginEpoch - 1 days); - - //EPOCH NOT STARTED - //vm.expectRevert(Controller.EpochNotStarted.selector); - controller.triggerNullEpoch(vaultFactory.marketIndex(), endEpoch); - vm.stopPrank(); - } - function testFailNullEpochRevNOTZEROTVL() public { - //need to fix triggerNullEpoch - vm.startPrank(admin); - vm.deal(alice, DEGEN_MULTIPLIER * AMOUNT); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - - address hedge = vaultFactory.getVaults(1)[0]; - Vault vHedge = Vault(hedge); - address risk = vaultFactory.getVaults(1)[1]; - Vault vRisk = Vault(risk); - - vm.startPrank(alice); - vHedge.depositETH{value: AMOUNT}(endEpoch, alice); - vRisk.depositETH{value: AMOUNT}(endEpoch, alice); - vm.stopPrank(); - - vm.startPrank(admin); - vm.warp(beginEpoch + 1); - - //EPOCH NOT ZERO TVL - //vm.expectRevert(Controller.VaultNotZeroTVL.selector); - controller.triggerNullEpoch(vaultFactory.marketIndex(), endEpoch); - - vm.stopPrank(); - } - - function testFailNotStrikePrice() public { - //revert working as expected but expectRevert not working - vm.startPrank(admin); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.warp(endEpoch); - address hedge = vaultFactory.getVaults(1)[0]; - Vault vHedge = Vault(hedge); - //vm.expectRevert(abi.encodeWithSelector(Controller.PriceNotAtStrikePrice.selector, controller.getLatestPrice(vHedge.tokenInsured()))); - controller.triggerDepeg(vaultFactory.marketIndex(), endEpoch); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// VAULTFACTORY reverts //////////////////////////////////////////////////////////////*/ @@ -311,14 +225,6 @@ contract RevertTest is Helper { vm.stopPrank(); } - function testControllerNotSet() public { - vm.startPrank(admin); - VaultFactory testFactory = new VaultFactory(admin, WETH, admin); - vm.expectRevert(VaultFactory.ControllerNotSet.selector); - testFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, oracleFRAX, "y2kFRAX_99*"); - vm.stopPrank(); - } - /*/////////////////////////////////////////////////////////////// VAULT reverts @@ -332,14 +238,6 @@ contract RevertTest is Helper { vm.stopPrank(); } - function testFeeCannotBeZero() public { - vm.startPrank(admin); - Vault testVault = new Vault(tokenFRAX, "Frax stable", "FRAX", admin, oracleFRAX, VAULT_STRIKE_PRICE, address(controller)); - vm.expectRevert(Vault.FeeCannotBe0.selector); - testVault.createAssets(beginEpoch, endEpoch, 0); - vm.stopPrank(); - } - function testVaultMarketEpochDoesNotExist() public { vm.startPrank(admin); Vault testVault = new Vault(tokenFRAX, "Frax stable", "FRAX", admin, oracleFRAX, VAULT_STRIKE_PRICE, address(controller)); @@ -461,22 +359,21 @@ contract RevertTest is Helper { vm.stopPrank(); } - // function testVaultAddressZero() public { - // vm.startPrank(admin); - // Vault testVault = new Vault(tokenFRAX, "Frax stable", "FRAX", admin, oracleFRAX, VAULT_STRIKE_PRICE, address(controller)); - // vm.stopPrank(); + function testVaultAddressZero() public { + vm.startPrank(admin); + Vault testVault = new Vault(tokenFRAX, "Frax stable", "FRAX", admin, oracleFRAX, VAULT_STRIKE_PRICE, address(controller)); + vm.stopPrank(); - // vm.startPrank(admin); - // vm.warp(endEpoch); - // //vm.expectRevert(Vault.AddressZero.selector); - // vaultFactory.changeTreasury(address(0)); - // vm.stopPrank(); + vm.startPrank(admin); + vm.expectRevert(Vault.AddressZero.selector); + testVault.changeTreasury(address(0)); + vm.stopPrank(); - // vm.startPrank(admin); - // //vm.expectRevert(Vault.AddressZero.selector); - // testVault.changeController(address(0)); - // vm.stopPrank(); - // } + vm.startPrank(admin); + vm.expectRevert(Vault.AddressZero.selector); + testVault.changeController(address(0)); + vm.stopPrank(); + } function testFailZeroValue() public { vm.deal(alice, 20 ether); @@ -500,22 +397,6 @@ contract RevertTest is Helper { vm.stopPrank(); } - function testFailTimelocked() public { - //forge can't compare reverts if the function that is called is not on the same contract as the revert) - //(can only make internal comparisons) - //all cases revert TimeLocked() - vm.startPrank(admin); - vaultFactory.createNewMarket(FEE, tokenFRAX, DEPEG_AAA, beginEpoch, endEpoch, address(oracleFRAX), "y2kFRAX_99*"); - vm.warp(block.timestamp + 6 days); - vm.expectRevert(Vault.TimeLocked.selector); - vaultFactory.changeTreasury(alice, vaultFactory.marketIndex()); - vm.expectRevert(Vault.TimeLocked.selector); - vaultFactory.changeController(vaultFactory.marketIndex(), alice); - vm.expectRevert(Vault.TimeLocked.selector); - vaultFactory.changeTimewindow(vaultFactory.marketIndex(), 1); - vm.stopPrank(); - } - /*/////////////////////////////////////////////////////////////// REWARDSFACTORY reverts //////////////////////////////////////////////////////////////*/