Skip to content

Commit

Permalink
- Refactored Vault contract to Vault and VaultDelegate for easier…
Browse files Browse the repository at this point in the history
… upgradability

- Renamed `callerMustBe...` to `senderMustBe...`
- Refactored tests
  • Loading branch information
heyaibi committed Mar 25, 2022
1 parent 7b68560 commit e0fe849
Show file tree
Hide file tree
Showing 31 changed files with 1,071 additions and 428 deletions.
2 changes: 1 addition & 1 deletion contracts/core/cxToken/cxToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract cxToken is ICxToken, Recoverable, ERC20 {

require(amount > 0, "Please specify amount");
require(key == coverKey, "Invalid cover");
s.callerMustBePolicyContract();
s.senderMustBePolicyContract();

super._mint(to, amount);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/core/cxToken/cxTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ contract cxTokenFactory is ICxTokenFactory, Recoverable {
// @suppress-acl Can only be called by the latest policy contract
s.mustNotBePaused();
s.mustBeValidCoverKey(key);
s.callerMustBePolicyContract();
s.senderMustBePolicyContract();

require(expiryDate > 0, "Please specify expiry date");

Expand Down
26 changes: 26 additions & 0 deletions contracts/core/delegates/VaultDelegate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Neptune Mutual Protocol (https://neptunemutual.com)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.0;

import "./VaultDelegateWithFlashLoan.sol";

/**
* @title Cover Vault for Liquidity
* @dev Liquidity providers can earn fees by adding stablecoin liquidity
* to any cover contract. The cover pool is collectively owned by liquidity providers
* where fees automatically get accumulated and compounded. <br /> <br />
* **Fees** <br />
*
* - Cover fees paid in stablecoin get added to the liquidity pool.
* - The protocol supplies a small portion of idle assets to lending protocols (v2).
* - Flash loan interest also gets added back to the pool.
* - To protect liquidity providers from cover incidents, they can redeem up to 25% of the cover payouts through NPM provision.
* - To protect liquidity providers from cover incidents, they can redeem up to 25% of the cover payouts through `reassurance token` allocation.
*/
contract VaultDelegate is VaultDelegateWithFlashLoan {
using ProtoUtilV1 for IStore;
using ValidationLibV1 for IStore;
using VaultLibV1 for IStore;

constructor(IStore store) VaultDelegateBase(store) {} // solhint-disable-line
}
256 changes: 256 additions & 0 deletions contracts/core/delegates/VaultDelegateBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// Neptune Mutual Protocol (https://neptunemutual.com)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.0;

import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "../Recoverable.sol";
import "../../interfaces/IVaultDelegate.sol";
import "../../libraries/ProtoUtilV1.sol";
import "../../libraries/CoverUtilV1.sol";
import "../../libraries/VaultLibV1.sol";
import "../../libraries/ValidationLibV1.sol";
import "../../libraries/StrategyLibV1.sol";

/**
* @title Vault POD (Proof of Deposit)
* @dev The VaultPod has `_mintPods` and `_redeemPods` features which enables
* POD minting and burning on demand. <br /> <br />
*
* **How Does This Work?**
*
* When you add liquidity to the Vault,
* PODs are minted representing your proportional share of the pool.
* Similarly, when you redeem your PODs, you get your proportional
* share of the Vault liquidity back, burning the PODs.
*/
abstract contract VaultDelegateBase is IVaultDelegate, Recoverable {
using ProtoUtilV1 for bytes;
using ProtoUtilV1 for IStore;
using VaultLibV1 for IStore;
using ValidationLibV1 for IStore;
using RoutineInvokerLibV1 for IStore;
using StoreKeyUtil for IStore;
using StrategyLibV1 for IStore;
using CoverUtilV1 for IStore;
using NTransferUtilV2 for IERC20;

/**
* @dev Constructs this contract
* @param store Provide the store contract instance
*/
constructor(IStore store) Recoverable(store) {} // solhint-disable-line

function preTransferGovernance(
address caller,
bytes32 coverKey,
address, /*to*/
uint256 amount
) external override nonReentrant returns (address stablecoin) {
require(amount > 0, "Please specify amount");

s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);
s.callerMustBeClaimsProcessorContract(caller);

stablecoin = s.getStablecoin();
}

function postTransferGovernance(
address, /*caller*/
bytes32 coverKey,
address, /*to*/
uint256 /*amount*/
) external view override {
s.senderMustBeVaultContract(coverKey);
// @suppress-reentrancy The `postTransferGovernance` hook is executed under the same context of `preTransferGovernance`.
// @note: do not update state and liquidity since `transferGovernance` is an internal contract-only function
}

function preTransferToStrategy(
address caller,
IERC20 token,
bytes32 coverKey,
bytes32 strategyName,
uint256 amount
) external override nonReentrant {
require(amount > 0, "Please specify amount");

s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);
s.callerMustBeSpecificStrategyContract(caller, strategyName);

s.preTransferToStrategyInternal(token, coverKey, strategyName, amount);
}

function postTransferToStrategy(
address, /*caller*/
IERC20, /*token*/
bytes32 coverKey,
bytes32, /*strategyName*/
uint256 /*amount*/
) external view override {
s.senderMustBeVaultContract(coverKey);
// @suppress-reentrancy The `postTransferToStrategy` hook is executed under the same context of `preTransferToStrategy`.
// @note: do not update state and liquidity since `transferToStrategy` itself is a part of the state update
}

function preReceiveFromStrategy(
address caller,
IERC20, /*token*/
bytes32 coverKey,
bytes32 strategyName,
uint256 amount
) external override nonReentrant {
require(amount > 0, "Please specify amount");

s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);
s.callerMustBeSpecificStrategyContract(caller, strategyName);
}

function postReceiveFromStrategy(
address caller,
IERC20 token,
bytes32 coverKey,
bytes32 strategyName,
uint256 amount
) external override returns (uint256 income, uint256 loss) {
s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);
s.callerMustBeStrategyContract(caller);

(income, loss) = s.postReceiveFromStrategyInternal(token, coverKey, strategyName, amount);
// @suppress-reentrancy The `postReceiveFromStrategy` hook is executed under the same context of `preReceiveFromStrategy`.
// @note: do not update state and liquidity since `receiveFromStrategy` itself is a part of the state update
}

/**
* @dev Adds liquidity to the specified cover contract
* @param coverKey Enter the cover key
* @param amount Enter the amount of liquidity token to supply.
* @param npmStakeToAdd Enter the amount of NPM token to stake.
*/
function preAddLiquidity(
address caller,
bytes32 coverKey,
uint256 amount,
uint256 npmStakeToAdd
) external override nonReentrant returns (uint256 podsToMint, uint256 previousNpmStake) {
require(amount > 0, "Please specify amount");
s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);

(podsToMint, previousNpmStake) = s.preAddLiquidityInternal(coverKey, msg.sender, caller, amount, npmStakeToAdd);
}

function postAddLiquidity(
address, /*caller*/
bytes32 coverKey,
uint256, /*amount*/
uint256 /*npmStakeToAdd*/
) external override {
s.senderMustBeVaultContract(coverKey);
s.updateStateAndLiquidity(coverKey);

// @suppress-reentrancy The `postAddLiquidity` hook is executed under the same context of `preAddLiquidity`.
}

function accrueInterestImplementation(address caller, bytes32 coverKey) external override {
s.senderMustBeVaultContract(coverKey);
AccessControlLibV1.callerMustBeLiquidityManager(s, caller);

s.accrueInterestInternal(coverKey);
}

/**
* @dev Removes liquidity from the specified cover contract
* @param coverKey Enter the cover key
* @param podsToRedeem Enter the amount of pods to redeem
* @param npmStakeToRemove Enter the amount of NPM stake to remove.
*/
function preRemoveLiquidity(
address, /*caller*/
bytes32 coverKey,
uint256 podsToRedeem,
uint256 npmStakeToRemove,
bool exit
) external override nonReentrant returns (address stablecoin, uint256 stablecoinToRelease) {
require(podsToRedeem > 0, "Please specify amount");

s.mustNotBePaused();
s.senderMustBeVaultContract(coverKey);

return s.preRemoveLiquidityInternal(coverKey, msg.sender, podsToRedeem, npmStakeToRemove, exit);
}

function postRemoveLiquidity(
address, /*caller*/
bytes32 coverKey,
uint256, /*podsToRedeem*/
uint256, /*npmStakeToRemove*/
bool /*exit*/
) external override {
s.senderMustBeVaultContract(coverKey);
s.updateStateAndLiquidity(coverKey);

// @suppress-reentrancy The `postRemoveLiquidity` hook is executed under the same context of `preRemoveLiquidity`.
}

/**
* @dev Calculates the amount of PODS to mint for the given amount of liquidity to transfer
*/
function calculatePodsImplementation(bytes32 coverKey, uint256 forStablecoinUnits) external view override returns (uint256) {
s.senderMustBeVaultContract(coverKey);
return s.calculatePodsInternal(coverKey, msg.sender, forStablecoinUnits);
}

/**
* @dev Calculates the amount of stablecoins to withdraw for the given amount of PODs to redeem
*/
function calculateLiquidityImplementation(bytes32 coverKey, uint256 podsToBurn) external view override returns (uint256) {
s.senderMustBeVaultContract(coverKey);
return s.calculateLiquidityInternal(coverKey, msg.sender, podsToBurn);
}

/**
* @dev Returns the stablecoin balance of this vault
* This also includes amounts lent out in lending strategies
*/
function getStablecoinBalanceOfImplementation(bytes32 coverKey) external view override returns (uint256) {
s.senderMustBeVaultContract(coverKey);
return s.getStablecoinBalanceOfInternal(coverKey);
}

/**
* @dev Gets information of a given vault by the cover key
* @param you The address for which the info will be customized
* @param values[0] totalPods --> Total PODs in existence
* @param values[1] balance --> Stablecoins held in the vault
* @param values[2] extendedBalance --> Stablecoins lent outside of the protocol
* @param values[3] totalReassurance -- > Total reassurance for this cover
* @param values[4] myPodBalance --> Your POD Balance
* @param values[5] myDeposits --> Sum of your deposits (in stablecoin)
* @param values[6] myWithdrawals --> Sum of your withdrawals (in stablecoin)
* @param values[7] myShare --> My share of the liquidity pool (in stablecoin)
* @param values[8] withdrawalOpen --> The timestamp when withdrawals are opened
* @param values[9] withdrawalClose --> The timestamp when withdrawals are closed again
*/
function getInfoImplementation(bytes32 coverKey, address you) external view override returns (uint256[] memory values) {
s.senderMustBeVaultContract(coverKey);
return s.getInfoInternal(coverKey, msg.sender, you);
}

/**
* @dev Version number of this contract
*/
function version() external pure override returns (bytes32) {
return "v0.1";
}

/**
* @dev Name of this contract
*/
function getName() external pure override returns (bytes32) {
return ProtoUtilV1.CNAME_VAULT_DELEGATE;
}
}
Loading

0 comments on commit e0fe849

Please sign in to comment.