diff --git a/src/Controller.sol b/src/Controller.sol index 8abc1a3e..d0cac3fb 100644 --- a/src/Controller.sol +++ b/src/Controller.sol @@ -28,7 +28,6 @@ contract Controller { error EpochExpired(); error OraclePriceZero(); error RoundIDOutdated(); - error TimestampZero(); error EpochNotExist(); error EpochNotExpired(); @@ -62,43 +61,6 @@ contract Controller { } /* solhint-enable var-name-mixedcase */ - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /** @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 //////////////////////////////////////////////////////////////*/ @@ -131,12 +93,33 @@ 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.idFinalTVL(epochEnd) != 0) revert NotZeroTVL(); @@ -180,9 +163,6 @@ 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(); @@ -191,6 +171,11 @@ 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(); @@ -230,6 +215,51 @@ 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 { + if( + vaultFactory.getVaults(marketIndex).length != VAULTS_LENGTH) + revert MarketDoesNotExist(marketIndex); + if( + block.timestamp >= epochEnd) + revert EpochExpired(); + + address[] memory vaultsAddress = vaultFactory.getVaults(marketIndex); + + Vault insrVault = Vault(vaultsAddress[0]); + Vault riskVault = Vault(vaultsAddress[1]); + + 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.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); + if(riskVault.idFinalTVL(epochEnd) != 0) + revert NotZeroTVL(); + + //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)); + } + if(riskVault.totalAssets(epochEnd) == 0){ + insrVault.endEpoch(epochEnd); + riskVault.endEpoch(epochEnd); + + insrVault.setClaimTVL(epochEnd, insrVault.idFinalTVL(epochEnd) ); + riskVault.setClaimTVL(epochEnd, 0); + } + } /*////////////////////////////////////////////////////////////// GETTERS @@ -290,10 +320,7 @@ 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 24bbe962..04bf277c 100644 --- a/src/SemiFungibleVault.sol +++ b/src/SemiFungibleVault.sol @@ -128,7 +128,9 @@ 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); + function totalAssets(uint256 _id) public view virtual returns (uint256){ + return totalSupply(_id); + } /** @notice Shows assets conversion output from withdrawing assets diff --git a/src/Vault.sol b/src/Vault.sol index 58e7e57d..585aea6b 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -53,6 +53,7 @@ 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 @@ -188,7 +189,7 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { } /** - @notice Withdraw entitled deposited assets, checking if a depeg event //TODO add GOV token rewards + @notice Withdraw entitled deposited assets, checking if a depeg event @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; @@ -212,14 +213,19 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { isApprovedForAll(owner, msg.sender) == false) revert OwnerDidNotAuthorize(msg.sender, owner); - uint256 entitledShares = previewWithdraw(id, assets); + uint256 entitledShares; _burn(owner, id, assets); - //Taking fee from the amount - uint256 feeValue = calculateWithdrawalFeeValue(entitledShares, id); - entitledShares = entitledShares - feeValue; - asset.transfer(treasury, feeValue); - + if(epochNull[id] == false) { + //Taking fee from the amount + entitledShares = previewWithdraw(id, assets); + uint256 feeValue = calculateWithdrawalFeeValue(entitledShares, id); + entitledShares = entitledShares - feeValue; + asset.transfer(treasury, feeValue); + } + else{ + entitledShares = assets; + } emit Withdraw(msg.sender, receiver, owner, id, assets, entitledShares); asset.transfer(receiver, entitledShares); @@ -357,6 +363,10 @@ contract Vault is SemiFungibleVault, ReentrancyGuard { asset.transfer(_counterparty, idFinalTVL[id]); } + function setEpochNull(uint256 id) public onlyController { + epochNull[id] = true; + } + /*/////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/