diff --git a/abis/ICxTokenFactory.json b/abis/ICxTokenFactory.json
index 4f0e1d13..29cb7d6d 100644
--- a/abis/ICxTokenFactory.json
+++ b/abis/ICxTokenFactory.json
@@ -19,6 +19,18 @@
"internalType": "uint256",
"name": "expiryDate",
"type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "symbol",
+ "type": "string"
}
],
"name": "CxTokenDeployed",
diff --git a/abis/IVault.json b/abis/IVault.json
index d51e88c4..67c6be67 100644
--- a/abis/IVault.json
+++ b/abis/IVault.json
@@ -24,6 +24,44 @@
"name": "Approval",
"type": "event"
},
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "bytes32",
+ "name": "key",
+ "type": "bytes32"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "Entered",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "bytes32",
+ "name": "key",
+ "type": "bytes32"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "Exited",
+ "type": "event"
+ },
{
"anonymous": false,
"inputs": [
@@ -93,6 +131,44 @@
"name": "InterestAccrued",
"type": "event"
},
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "NPMStaken",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "NPMUnstaken",
+ "type": "event"
+ },
{
"anonymous": false,
"inputs": [
@@ -435,19 +511,6 @@
"stateMutability": "view",
"type": "function"
},
- {
- "inputs": [],
- "name": "lqt",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
{
"inputs": [
{
@@ -504,6 +567,19 @@
"stateMutability": "nonpayable",
"type": "function"
},
+ {
+ "inputs": [],
+ "name": "sc",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"inputs": [],
"name": "totalSupply",
diff --git a/abis/IVaultDelegate.json b/abis/IVaultDelegate.json
new file mode 100644
index 00000000..265e1624
--- /dev/null
+++ b/abis/IVaultDelegate.json
@@ -0,0 +1,641 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ }
+ ],
+ "name": "accrueInterestImplementation",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "podsToBurn",
+ "type": "uint256"
+ }
+ ],
+ "name": "calculateLiquidityImplementation",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "forStablecoinUnits",
+ "type": "uint256"
+ }
+ ],
+ "name": "calculatePodsImplementation",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getFlashFee",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "forAccount",
+ "type": "address"
+ }
+ ],
+ "name": "getInfoImplementation",
+ "outputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "result",
+ "type": "uint256[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ }
+ ],
+ "name": "getMaxFlashLoan",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getName",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getStablecoinBalanceOfImplementation",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "npmStake",
+ "type": "uint256"
+ }
+ ],
+ "name": "postAddLiquidity",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "contract IERC3156FlashBorrower",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "postFlashLoan",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "strategyName",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "postReceiveFromStrategy",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "income",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loss",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "npmStake",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "exit",
+ "type": "bool"
+ }
+ ],
+ "name": "postRemoveLiquidity",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "postTransferGovernance",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "strategyName",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "postTransferToStrategy",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "npmStake",
+ "type": "uint256"
+ }
+ ],
+ "name": "preAddLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "podsToMint",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "previousNPMStake",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "contract IERC3156FlashBorrower",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "preFlashLoan",
+ "outputs": [
+ {
+ "internalType": "contract IERC20",
+ "name": "stablecoin",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "fee",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "protocolFee",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "strategyName",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "preReceiveFromStrategy",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "npmStake",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "exit",
+ "type": "bool"
+ }
+ ],
+ "name": "preRemoveLiquidity",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "stablecoin",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "stableCoinToRelease",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "preTransferGovernance",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "stablecoin",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "caller",
+ "type": "address"
+ },
+ {
+ "internalType": "contract IERC20",
+ "name": "token",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "coverKey",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "strategyName",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "preTransferToStrategy",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "version",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/contracts/core/claims/Processor.sol b/contracts/core/claims/Processor.sol
index b4ddc577..6b009a07 100644
--- a/contracts/core/claims/Processor.sol
+++ b/contracts/core/claims/Processor.sol
@@ -130,11 +130,13 @@ contract Processor is IClaimsProcessor, Recoverable {
if (key > 0) {
previous = s.getUintByKeys(ProtoUtilV1.NS_CLAIM_PERIOD, key);
s.setUintByKeys(ProtoUtilV1.NS_CLAIM_PERIOD, key, value);
- } else {
- previous = s.getUintByKey(ProtoUtilV1.NS_CLAIM_PERIOD);
- s.setUintByKey(ProtoUtilV1.NS_CLAIM_PERIOD, value);
+ emit ClaimPeriodSet(key, previous, value);
+ return;
}
+ previous = s.getUintByKey(ProtoUtilV1.NS_CLAIM_PERIOD);
+ s.setUintByKey(ProtoUtilV1.NS_CLAIM_PERIOD, value);
+
emit ClaimPeriodSet(key, previous, value);
}
diff --git a/contracts/core/cxToken/cxToken.sol b/contracts/core/cxToken/cxToken.sol
index 212ab21f..50cacea6 100644
--- a/contracts/core/cxToken/cxToken.sol
+++ b/contracts/core/cxToken/cxToken.sol
@@ -23,7 +23,7 @@ contract cxToken is ICxToken, Recoverable, ERC20 {
using ValidationLibV1 for IStore;
bytes32 public immutable override coverKey;
- uint256 public immutable override createdOn;
+ uint256 public immutable override createdOn = block.timestamp; // solhint-disable-line
uint256 public immutable override expiresOn;
/**
@@ -40,8 +40,6 @@ contract cxToken is ICxToken, Recoverable, ERC20 {
string memory symbol
) ERC20(name, symbol) Recoverable(store) {
coverKey = key;
-
- createdOn = block.timestamp; // solhint-disable-line
expiresOn = expiry;
}
@@ -62,7 +60,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);
}
diff --git a/contracts/core/cxToken/cxTokenFactory.sol b/contracts/core/cxToken/cxTokenFactory.sol
index 05f62309..0e354fbf 100644
--- a/contracts/core/cxToken/cxTokenFactory.sol
+++ b/contracts/core/cxToken/cxTokenFactory.sol
@@ -50,11 +50,14 @@ 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");
- (bytes memory bytecode, bytes32 salt) = cxTokenFactoryLibV1.getByteCode(s, key, expiryDate, _getTokenName(key), _getTokenSymbol());
+ string memory name = _getTokenName(key);
+ string memory symbol = _getTokenSymbol();
+
+ (bytes memory bytecode, bytes32 salt) = cxTokenFactoryLibV1.getByteCode(s, key, expiryDate, name, symbol);
require(s.getAddress(salt) == address(0), "Already deployed");
@@ -79,7 +82,7 @@ contract cxTokenFactory is ICxTokenFactory, Recoverable {
s.setBoolByKeys(ProtoUtilV1.NS_COVER_CXTOKEN, deployed, true);
s.setAddressArrayByKeys(ProtoUtilV1.NS_COVER_CXTOKEN, key, deployed);
- emit CxTokenDeployed(key, deployed, expiryDate);
+ emit CxTokenDeployed(key, deployed, expiryDate, name, symbol);
}
/**
diff --git a/contracts/core/delegates/VaultDelegate.sol b/contracts/core/delegates/VaultDelegate.sol
new file mode 100644
index 00000000..20fd65cc
--- /dev/null
+++ b/contracts/core/delegates/VaultDelegate.sol
@@ -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.
+ * **Fees**
+ *
+ * - 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
+}
diff --git a/contracts/core/delegates/VaultDelegateBase.sol b/contracts/core/delegates/VaultDelegateBase.sol
new file mode 100644
index 00000000..bf45b4fc
--- /dev/null
+++ b/contracts/core/delegates/VaultDelegateBase.sol
@@ -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.
+ *
+ * **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;
+ }
+}
diff --git a/contracts/core/delegates/VaultDelegateWithFlashLoan.sol b/contracts/core/delegates/VaultDelegateWithFlashLoan.sol
new file mode 100644
index 00000000..57a90c3d
--- /dev/null
+++ b/contracts/core/delegates/VaultDelegateWithFlashLoan.sol
@@ -0,0 +1,98 @@
+// Neptune Mutual Protocol (https://neptunemutual.com)
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity 0.8.0;
+
+import "./VaultDelegateBase.sol";
+
+/**
+ * @title With Flash Loan Contract
+ * @dev WithFlashLoan contract implements `EIP-3156 Flash Loan`.
+ * Using flash loans, you can borrow up to the total available amount of
+ * the stablecoin liquidity available in this cover liquidity pool.
+ * You need to return back the borrowed amount + fee in the same transaction.
+ * The function `flashFee` enables you to check, in advance, fee that
+ * you need to pay to take out the loan.
+ */
+abstract contract VaultDelegateWithFlashLoan is VaultDelegateBase {
+ using ProtoUtilV1 for IStore;
+ using StoreKeyUtil for IStore;
+ using ValidationLibV1 for IStore;
+ using VaultLibV1 for IStore;
+ using NTransferUtilV2 for IERC20;
+ using RoutineInvokerLibV1 for IStore;
+
+ /**
+ * @dev The fee to be charged for a given loan.
+ * @param token The loan currency.
+ * @param amount The amount of tokens lent.
+ * @return The amount of `token` to be charged for the loan, on top of the returned principal.
+ */
+ function getFlashFee(
+ address, /*caller*/
+ bytes32 coverKey,
+ address token,
+ uint256 amount
+ ) external view override returns (uint256) {
+ s.senderMustBeVaultContract(coverKey);
+ return s.getFlashFeeInternal(coverKey, token, amount);
+ }
+
+ /**
+ * @dev The amount of currency available to be lent.
+ * @param token The loan currency.
+ * @return The amount of `token` that can be borrowed.
+ */
+ function getMaxFlashLoan(
+ address, /*caller*/
+ bytes32 coverKey,
+ address token
+ ) external view override returns (uint256) {
+ s.senderMustBeVaultContract(coverKey);
+ return s.getMaxFlashLoanInternal(coverKey, token);
+ }
+
+ function preFlashLoan(
+ address, /*caller*/
+ bytes32 coverKey,
+ IERC3156FlashBorrower, /*receiver*/
+ address token,
+ uint256 amount,
+ bytes calldata /*data*/
+ )
+ external
+ override
+ returns (
+ IERC20 stablecoin,
+ uint256 fee,
+ uint256 protocolFee
+ )
+ {
+ s.mustNotBePaused();
+ s.mustHaveNormalCoverStatus(coverKey);
+ s.senderMustBeVaultContract(coverKey);
+
+ stablecoin = IERC20(s.getStablecoin());
+
+ require(address(stablecoin) == token, "Unknown token");
+ require(amount > 0, "Loan too small");
+ require(fee > 0, "Fee too little");
+
+ s.setBoolByKeys(ProtoUtilV1.NS_COVER_HAS_FLASH_LOAN, coverKey, true);
+
+ (fee, protocolFee) = s.getFlashFeesInternal(coverKey, token, amount);
+ }
+
+ function postFlashLoan(
+ address, /*caller*/
+ bytes32 coverKey,
+ IERC3156FlashBorrower, /*receiver*/
+ address, /*token*/
+ uint256, /*amount*/
+ bytes calldata /*data*/
+ ) external override {
+ s.senderMustBeVaultContract(coverKey);
+
+ s.setBoolByKeys(ProtoUtilV1.NS_COVER_HAS_FLASH_LOAN, coverKey, false);
+ s.updateStateAndLiquidity(coverKey);
+ }
+}
diff --git a/contracts/core/governance/resolution/Resolvable.sol b/contracts/core/governance/resolution/Resolvable.sol
index 53f43046..a95ca0ea 100644
--- a/contracts/core/governance/resolution/Resolvable.sol
+++ b/contracts/core/governance/resolution/Resolvable.sol
@@ -88,15 +88,6 @@ abstract contract Resolvable is Finalization, IResolvable {
// back the original stake. No rewards.
CoverUtilV1.CoverStatus status = decision ? CoverUtilV1.CoverStatus.Claimable : CoverUtilV1.CoverStatus.FalseReporting;
- // Claim begins from the cooldown period added to the first resolution attempt date
- uint256 claimBeginsFrom = decision ? block.timestamp + cooldownPeriod : 0; // solhint-disable-line
-
- // Claim expires after the period specified by the cover creator.
- uint256 claimExpiresAt = decision ? claimBeginsFrom + s.getClaimPeriod(key) : 0;
-
- s.setUintByKeys(ProtoUtilV1.NS_CLAIM_BEGIN_TS, key, claimBeginsFrom);
- s.setUintByKeys(ProtoUtilV1.NS_CLAIM_EXPIRY_TS, key, claimExpiresAt);
-
// Status can change during `Emergency Resolution` attempt(s)
s.setStatusInternal(key, incidentDate, status);
@@ -108,6 +99,15 @@ abstract contract Resolvable is Finalization, IResolvable {
s.setUintByKeys(ProtoUtilV1.NS_RESOLUTION_DEADLINE, key, deadline);
}
+ // Claim begins when deadline timestamp is passed
+ uint256 claimBeginsFrom = decision ? deadline + 1 : 0;
+
+ // Claim expires after the period specified by the cover creator.
+ uint256 claimExpiresAt = decision ? claimBeginsFrom + s.getClaimPeriod(key) : 0;
+
+ s.setUintByKeys(ProtoUtilV1.NS_CLAIM_BEGIN_TS, key, claimBeginsFrom);
+ s.setUintByKeys(ProtoUtilV1.NS_CLAIM_EXPIRY_TS, key, claimExpiresAt);
+
s.updateStateAndLiquidity(key);
emit Resolved(key, incidentDate, deadline, decision, emergency, claimBeginsFrom, claimExpiresAt);
diff --git a/contracts/core/lifecycle/Cover.sol b/contracts/core/lifecycle/Cover.sol
index 24d2e9b3..56beaa5e 100644
--- a/contracts/core/lifecycle/Cover.sol
+++ b/contracts/core/lifecycle/Cover.sol
@@ -34,7 +34,7 @@ contract Cover is CoverBase {
function updateCover(bytes32 key, bytes32 info) external override nonReentrant {
s.mustNotBePaused();
s.mustHaveNormalCoverStatus(key);
- s.callerMustBeCoverOwnerOrAdmin(key);
+ s.senderMustBeCoverOwnerOrAdmin(key);
require(s.getBytes32ByKeys(ProtoUtilV1.NS_COVER_INFO, key) != info, "Duplicate content");
@@ -102,7 +102,7 @@ contract Cover is CoverBase {
s.mustNotBePaused();
s.mustHaveStoppedCoverStatus(key);
- s.callerMustBeCoverOwnerOrAdmin(key);
+ s.senderMustBeCoverOwnerOrAdmin(key);
address vault = s.deployVaultInternal(key);
emit VaultDeployed(key, vault);
@@ -152,7 +152,7 @@ contract Cover is CoverBase {
) external override nonReentrant {
s.mustNotBePaused();
AccessControlLibV1.mustBeCoverManager(s);
- s.callerMustBeCoverOwnerOrAdmin(key);
+ s.senderMustBeCoverOwnerOrAdmin(key);
s.updateCoverUsersWhitelistInternal(key, accounts, statuses);
}
diff --git a/contracts/core/lifecycle/CoverStake.sol b/contracts/core/lifecycle/CoverStake.sol
index cb5e9794..ea88744c 100644
--- a/contracts/core/lifecycle/CoverStake.sol
+++ b/contracts/core/lifecycle/CoverStake.sol
@@ -52,7 +52,7 @@ contract CoverStake is ICoverStake, Recoverable {
// @suppress-acl Can only be accessed by the latest cover contract
s.mustNotBePaused();
s.mustBeValidCoverKey(key);
- s.callerMustBeCoverContract();
+ s.senderMustBeCoverContract();
require(amount >= fee, "Invalid fee");
@@ -86,7 +86,7 @@ contract CoverStake is ICoverStake, Recoverable {
// @suppress-acl Can only be accessed by the latest cover contract
s.mustNotBePaused();
s.mustBeValidCoverKey(key);
- s.callerMustBeCoverContract();
+ s.senderMustBeCoverContract();
uint256 drawingPower = _getDrawingPower(key, account);
require(amount > 0, "Please specify amount");
diff --git a/contracts/core/liquidity/Vault.sol b/contracts/core/liquidity/Vault.sol
index 37c10a71..bb64e1b0 100644
--- a/contracts/core/liquidity/Vault.sol
+++ b/contracts/core/liquidity/Vault.sol
@@ -1,30 +1,34 @@
// Neptune Mutual Protocol (https://neptunemutual.com)
// SPDX-License-Identifier: BUSL-1.1
-pragma solidity 0.8.0;
-
+import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
+import "openzeppelin-solidity/contracts/interfaces/IERC3156FlashLender.sol";
import "./WithFlashLoan.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.
- * **Fees**
- *
- * - 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.
- */
+pragma solidity 0.8.0;
+
contract Vault is WithFlashLoan {
using ProtoUtilV1 for IStore;
- using ValidationLibV1 for IStore;
- using VaultLibV1 for IStore;
+ using RegistryLibV1 for IStore;
+ using NTransferUtilV2 for IERC20;
constructor(
IStore store,
bytes32 coverKey,
IERC20 liquidityToken
- ) VaultBase(store, coverKey, liquidityToken) {} // solhint-disable-line
+ ) VaultBase(store, coverKey, liquidityToken) {}
+
+ /**
+ * @dev For further details, check delegate contract's documentation.
+ */
+ function getInfo(address you) external view override returns (uint256[] memory values) {
+ return delgate().getInfoImplementation(key, you);
+ }
+
+ function version() external pure override returns (bytes32) {
+ return "v0.1";
+ }
+
+ function getName() external pure override returns (bytes32) {
+ return ProtoUtilV1.CNAME_LIQUIDITY_VAULT;
+ }
}
diff --git a/contracts/core/liquidity/VaultBase.sol b/contracts/core/liquidity/VaultBase.sol
index c798b59e..cb433bef 100644
--- a/contracts/core/liquidity/VaultBase.sol
+++ b/contracts/core/liquidity/VaultBase.sol
@@ -1,229 +1,35 @@
// Neptune Mutual Protocol (https://neptunemutual.com)
// SPDX-License-Identifier: BUSL-1.1
-pragma solidity 0.8.0;
-
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "../Recoverable.sol";
+import "../../interfaces/IVaultDelegate.sol";
import "../../interfaces/IVault.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.
- *
- * **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 VaultBase is IVault, Recoverable, ERC20 {
- using ProtoUtilV1 for bytes;
+pragma solidity 0.8.0;
+
+abstract contract VaultBase is ERC20, Recoverable, IVault {
using ProtoUtilV1 for IStore;
- using VaultLibV1 for IStore;
- using ValidationLibV1 for IStore;
- using StoreKeyUtil for IStore;
- using StrategyLibV1 for IStore;
- using CoverUtilV1 for IStore;
+ using RegistryLibV1 for IStore;
using NTransferUtilV2 for IERC20;
- using RoutineInvokerLibV1 for IStore;
bytes32 public override key;
- address public override lqt;
- uint256 private _transferToStrategyEntry = 0;
- uint256 private _receiveFromStrategyEntry = 0;
+ address public override sc;
- /**
- * @dev Constructs this contract
- * @param store Provide the store contract instance
- * @param coverKey Enter the cover key or cover this contract is related to
- * @param liquidityToken Provide the liquidity token instance for this Vault
- */
constructor(
IStore store,
bytes32 coverKey,
- IERC20 liquidityToken
- ) ERC20(VaultLibV1.getPodTokenNameInternal(coverKey), "POD") Recoverable(store) {
+ IERC20 stablecoin
+ ) ERC20(_getTokenName(coverKey), "POD") Recoverable(store) {
key = coverKey;
- lqt = address(liquidityToken);
- }
-
- function transferGovernance(
- bytes32 coverKey,
- address to,
- uint256 amount
- ) external override nonReentrant {
- s.mustNotBePaused();
- s.callerMustBeClaimsProcessorContract();
-
- require(coverKey == key, "Forbidden");
- require(amount > 0, "Please specify amount");
-
- // @suppress-malicious-erc20 `lqt` can't be manipulated via user input.
- IERC20(lqt).ensureTransfer(to, amount);
- emit GovernanceTransfer(to, amount);
- }
-
- function transferToStrategy(
- IERC20 token,
- bytes32 coverKey,
- bytes32 strategyName,
- uint256 amount
- ) external override {
- // @suppress-reentrancy Custom reentrancy guard implemented
- require(_transferToStrategyEntry == 0, "Access is denied");
- require(amount > 0, "Please specify amount");
-
- _transferToStrategyEntry = 1;
-
- s.mustNotBePaused();
- s.callerMustBeStrategyContract();
- require(coverKey == key, "Forbidden");
-
- // @suppress-malicious-erc20 `token` can only be specified by strategy contract.
- s.transferToStrategyInternal(token, coverKey, strategyName, amount);
- emit StrategyTransfer(address(token), msg.sender, strategyName, amount);
- _transferToStrategyEntry = 0;
- }
-
- function receiveFromStrategy(
- IERC20 token,
- bytes32 coverKey,
- bytes32 strategyName,
- uint256 amount
- ) external override {
- // @suppress-reentrancy Custom reentrancy guard implemented
- require(_receiveFromStrategyEntry == 0, "Access is denied");
- require(amount > 0, "Please specify amount");
-
- _receiveFromStrategyEntry = 1;
-
- s.mustNotBePaused();
- s.callerMustBeStrategyContract();
- require(coverKey == key, "Forbidden");
-
- // @suppress-malicious-erc20 `token` can only be specified by strategy contract.
- (uint256 income, uint256 loss) = s.receiveFromStrategyInternal(token, coverKey, strategyName, amount);
-
- emit StrategyReceipt(address(token), msg.sender, strategyName, amount, income, loss);
- _receiveFromStrategyEntry = 0;
- }
-
- /**
- * @dev Returns the stablecoin balance of this vault
- * This also includes amounts lent out in lending strategies
- */
- function getStablecoinBalanceOf() external view override returns (uint256) {
- return s.getStablecoinBalanceOfInternal(key);
- }
-
- /**
- * @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 addLiquidity(
- bytes32 coverKey,
- uint256 amount,
- uint256 npmStakeToAdd
- ) external override nonReentrant {
- // @suppress-acl Marking this as publicly accessible
- s.mustNotBePaused();
- s.mustHaveNormalCoverStatus(key);
-
- require(coverKey == key, "Forbidden");
- require(amount > 0, "Please specify amount");
-
- uint256 podsToMint = s.addLiquidityInternal(coverKey, address(this), lqt, msg.sender, amount, npmStakeToAdd);
- super._mint(msg.sender, podsToMint);
-
- s.updateStateAndLiquidity(key);
-
- emit PodsIssued(msg.sender, podsToMint, amount);
- }
-
- function accrueInterest() external override nonReentrant {
- s.mustNotBePaused();
- AccessControlLibV1.mustBeLiquidityManager(s);
- s.accrueInterestInternal(key);
-
- emit InterestAccrued(key);
- }
-
- /**
- * @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 removeLiquidity(
- bytes32 coverKey,
- uint256 podsToRedeem,
- uint256 npmStakeToRemove,
- bool exit
- ) external override nonReentrant {
- // @suppress-acl Marking this as publicly accessible
- s.mustNotBePaused();
- s.mustBeAccrued(coverKey);
-
- require(podsToRedeem > 0, "Please specify amount");
-
- require(coverKey == key, "Forbidden");
- uint256 released = s.removeLiquidityInternal(coverKey, address(this), podsToRedeem, npmStakeToRemove, exit);
-
- emit PodsRedeemed(msg.sender, podsToRedeem, released);
- }
-
- /**
- * @dev Calculates the amount of PODS to mint for the given amount of liquidity to transfer
- */
- function calculatePods(uint256 forStablecoinUnits) external view override returns (uint256) {
- return s.calculatePodsInternal(key, address(this), forStablecoinUnits);
- }
-
- /**
- * @dev Calculates the amount of stablecoins to withdraw for the given amount of PODs to redeem
- */
- function calculateLiquidity(uint256 podsToBurn) external view override returns (uint256) {
- return s.calculateLiquidityInternal(key, address(this), lqt, podsToBurn);
- }
-
- /**
- * @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 getInfo(address you) external view override returns (uint256[] memory values) {
- return s.getInfoInternal(key, address(this), lqt, you);
+ sc = address(stablecoin);
}
- /**
- * @dev Version number of this contract
- */
- function version() external pure override returns (bytes32) {
- return "v0.1";
+ function _getTokenName(bytes32 coverKey) private pure returns (string memory) {
+ return string(abi.encodePacked(string(abi.encodePacked(coverKey)), "-pod"));
}
- /**
- * @dev Name of this contract
- */
- function getName() external pure override returns (bytes32) {
- return ProtoUtilV1.CNAME_LIQUIDITY_VAULT;
+ function delgate() public view returns (IVaultDelegate) {
+ address delegate = s.getVaultDelegate();
+ return IVaultDelegate(delegate);
}
}
diff --git a/contracts/core/liquidity/VaultFactory.sol b/contracts/core/liquidity/VaultFactory.sol
index 8d9bdb43..7d4445df 100644
--- a/contracts/core/liquidity/VaultFactory.sol
+++ b/contracts/core/liquidity/VaultFactory.sol
@@ -34,7 +34,7 @@ contract VaultFactory is IVaultFactory, Recoverable {
function deploy(IStore s, bytes32 key) external override nonReentrant returns (address addr) {
s.mustNotBePaused();
s.mustHaveNormalCoverStatus(key);
- s.callerMustBeCoverContract();
+ s.senderMustBeCoverContract();
(bytes memory bytecode, bytes32 salt) = VaultFactoryLibV1.getByteCode(s, key, s.getStablecoin());
diff --git a/contracts/core/liquidity/VaultLiquidity.sol b/contracts/core/liquidity/VaultLiquidity.sol
new file mode 100644
index 00000000..b99ec4c0
--- /dev/null
+++ b/contracts/core/liquidity/VaultLiquidity.sol
@@ -0,0 +1,158 @@
+// Neptune Mutual Protocol (https://neptunemutual.com)
+// SPDX-License-Identifier: BUSL-1.1
+import "./VaultBase.sol";
+import "hardhat/console.sol";
+
+pragma solidity 0.8.0;
+
+abstract contract VaultLiquidity is VaultBase {
+ using ProtoUtilV1 for IStore;
+ using RegistryLibV1 for IStore;
+ using NTransferUtilV2 for IERC20;
+
+ function transferGovernance(
+ bytes32 coverKey,
+ address to,
+ uint256 amount
+ ) external override nonReentrant {
+ require(coverKey == key, "Forbidden");
+
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+ address stablecoin = delgate().preTransferGovernance(msg.sender, coverKey, to, amount);
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+
+ IERC20(stablecoin).ensureTransfer(to, amount);
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+ delgate().postTransferGovernance(msg.sender, coverKey, to, amount);
+ emit GovernanceTransfer(to, amount);
+ }
+
+ /**
+ * @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 addLiquidity(
+ bytes32 coverKey,
+ uint256 amount,
+ uint256 npmStakeToAdd
+ ) external override nonReentrant {
+ // @suppress-acl Marking this as publicly accessible
+ require(coverKey == key, "Forbidden");
+
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+
+ (uint256 podsToMint, uint256 previousNpmStake) = delgate().preAddLiquidity(msg.sender, coverKey, amount, npmStakeToAdd);
+
+ require(podsToMint > 0, "Can't determine PODs");
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+
+ IERC20(sc).ensureTransferFrom(msg.sender, address(this), amount);
+
+ if (npmStakeToAdd > 0) {
+ IERC20(s.getNpmTokenAddress()).ensureTransferFrom(msg.sender, address(this), npmStakeToAdd);
+ }
+
+ super._mint(msg.sender, podsToMint);
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+
+ delgate().postAddLiquidity(msg.sender, coverKey, amount, npmStakeToAdd);
+
+ emit PodsIssued(msg.sender, podsToMint, amount);
+
+ if (previousNpmStake == 0) {
+ emit Entered(coverKey, msg.sender);
+ }
+
+ emit NPMStaken(msg.sender, npmStakeToAdd);
+ }
+
+ /**
+ * @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 removeLiquidity(
+ bytes32 coverKey,
+ uint256 podsToRedeem,
+ uint256 npmStakeToRemove,
+ bool exit
+ ) external override nonReentrant {
+ // @suppress-acl Marking this as publicly accessible
+ require(coverKey == key, "Forbidden");
+
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+ (address stablecoin, uint256 stablecoinToRelease) = delgate().preRemoveLiquidity(msg.sender, coverKey, podsToRedeem, npmStakeToRemove, exit);
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+ IERC20(address(this)).ensureTransferFrom(msg.sender, address(this), podsToRedeem);
+ IERC20(stablecoin).ensureTransfer(msg.sender, stablecoinToRelease);
+
+ // Unstake NPM tokens
+ if (npmStakeToRemove > 0) {
+ IERC20(s.getNpmTokenAddress()).ensureTransfer(msg.sender, npmStakeToRemove);
+ }
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+ delgate().postRemoveLiquidity(msg.sender, coverKey, podsToRedeem, npmStakeToRemove, exit);
+
+ emit PodsRedeemed(msg.sender, podsToRedeem, stablecoinToRelease);
+
+ if (exit) {
+ emit Exited(coverKey, msg.sender);
+ }
+
+ emit NPMUnstaken(msg.sender, npmStakeToRemove);
+ }
+
+ /**
+ * @dev Calculates the amount of PODS to mint for the given amount of liquidity to transfer
+ */
+ function calculatePods(uint256 forStablecoinUnits) external view override returns (uint256) {
+ return delgate().calculatePodsImplementation(key, forStablecoinUnits);
+ }
+
+ /**
+ * @dev Calculates the amount of stablecoins to withdraw for the given amount of PODs to redeem
+ */
+ function calculateLiquidity(uint256 podsToBurn) external view override returns (uint256) {
+ return delgate().calculateLiquidityImplementation(key, podsToBurn);
+ }
+
+ /**
+ * @dev Returns the stablecoin balance of this vault
+ * This also includes amounts lent out in lending strategies
+ */
+ function getStablecoinBalanceOf() external view override returns (uint256) {
+ return delgate().getStablecoinBalanceOfImplementation(key);
+ }
+
+ function accrueInterest() external override nonReentrant {
+ delgate().accrueInterestImplementation(msg.sender, key);
+ emit InterestAccrued(key);
+ }
+}
diff --git a/contracts/core/liquidity/VaultStrategy.sol b/contracts/core/liquidity/VaultStrategy.sol
new file mode 100644
index 00000000..3cdd8974
--- /dev/null
+++ b/contracts/core/liquidity/VaultStrategy.sol
@@ -0,0 +1,80 @@
+// Neptune Mutual Protocol (https://neptunemutual.com)
+// SPDX-License-Identifier: BUSL-1.1
+import "./VaultLiquidity.sol";
+
+pragma solidity 0.8.0;
+
+abstract contract VaultStrategy is VaultLiquidity {
+ using ProtoUtilV1 for IStore;
+ using RegistryLibV1 for IStore;
+ using NTransferUtilV2 for IERC20;
+
+ uint256 private _transferToStrategyEntry = 0;
+ uint256 private _receiveFromStrategyEntry = 0;
+
+ function transferToStrategy(
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external override {
+ // @suppress-reentrancy Custom reentrancy guard implemented
+ require(coverKey == key, "Forbidden");
+ require(_transferToStrategyEntry == 0, "Access is denied");
+ require(amount > 0, "Please specify amount");
+
+ _transferToStrategyEntry = 1;
+
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+ delgate().preTransferToStrategy(msg.sender, token, coverKey, strategyName, amount);
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+
+ token.ensureTransfer(msg.sender, amount);
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+ delgate().postTransferToStrategy(msg.sender, token, coverKey, strategyName, amount);
+
+ emit StrategyTransfer(address(token), msg.sender, strategyName, amount);
+ _transferToStrategyEntry = 0;
+ }
+
+ function receiveFromStrategy(
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external override {
+ // @suppress-reentrancy Custom reentrancy guard implemented
+ require(coverKey == key, "Forbidden");
+ require(_receiveFromStrategyEntry == 0, "Access is denied");
+ require(amount > 0, "Please specify amount");
+
+ _receiveFromStrategyEntry = 1;
+
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+ delgate().preReceiveFromStrategy(msg.sender, token, coverKey, strategyName, amount);
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+
+ token.ensureTransferFrom(msg.sender, address(this), amount);
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+ (uint256 income, uint256 loss) = delgate().postReceiveFromStrategy(msg.sender, token, coverKey, strategyName, amount);
+
+ emit StrategyReceipt(address(token), msg.sender, strategyName, amount, income, loss);
+ _receiveFromStrategyEntry = 0;
+ }
+}
diff --git a/contracts/core/liquidity/WithFlashLoan.sol b/contracts/core/liquidity/WithFlashLoan.sol
index 34478540..305d43a3 100644
--- a/contracts/core/liquidity/WithFlashLoan.sol
+++ b/contracts/core/liquidity/WithFlashLoan.sol
@@ -2,66 +2,59 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.0;
-import "./VaultBase.sol";
+import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/interfaces/IERC3156FlashLender.sol";
+import "./VaultStrategy.sol";
-/**
- * @title With Flash Loan Contract
- * @dev WithFlashLoan contract implements `EIP-3156 Flash Loan`.
- * Using flash loans, you can borrow up to the total available amount of
- * the stablecoin liquidity available in this cover liquidity pool.
- * You need to return back the borrowed amount + fee in the same transaction.
- * The function `flashFee` enables you to check, in advance, fee that
- * you need to pay to take out the loan.
- */
-abstract contract WithFlashLoan is VaultBase, IERC3156FlashLender {
+abstract contract WithFlashLoan is VaultStrategy, IERC3156FlashLender {
using ProtoUtilV1 for IStore;
- using StoreKeyUtil for IStore;
- using ValidationLibV1 for IStore;
- using VaultLibV1 for IStore;
+ using RegistryLibV1 for IStore;
using NTransferUtilV2 for IERC20;
- using RoutineInvokerLibV1 for IStore;
- /**
- * @dev The fee to be charged for a given loan.
- * @param token The loan currency.
- * @param amount The amount of tokens lent.
- * @return The amount of `token` to be charged for the loan, on top of the returned principal.
- */
- function flashFee(address token, uint256 amount) external view override returns (uint256) {
- return s.getFlashFeeInternal(token, amount);
- }
-
- /**
- * @dev The amount of currency available to be lent.
- * @param token The loan currency.
- * @return The amount of `token` that can be borrowed.
- */
- function maxFlashLoan(address token) external view override returns (uint256) {
- return s.getMaxFlashLoanInternal(token);
- }
-
- /**
- * @dev Initiate a flash loan.
- * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
- * @param token The loan currency.
- * @param amount The amount of tokens lent.
- * @param data Arbitrary data structure, intended to contain user-defined parameters.
- */
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external override nonReentrant returns (bool) {
- // @suppress-acl Marking this as publicly accessible
- // @suppress-address-trust-issue The instance of `token` can be trusted because we're ensuring it matches with the protocol stablecoin address.
- s.mustNotBePaused();
-
require(amount > 0, "Please specify amount");
- uint256 fee = s.flashLoanInternal(receiver, key, token, amount, data);
+ /******************************************************************************************
+ PRE
+ ******************************************************************************************/
+ (IERC20 stablecoin, uint256 fee, uint256 protocolFee) = delgate().preFlashLoan(msg.sender, key, receiver, token, amount, data);
+
+ /******************************************************************************************
+ BODY
+ ******************************************************************************************/
+ // @suppress-address-trust-issue, @suppress-malicious-erc20 `stablecoin` can't be manipulated via user input.
+ uint256 previousBalance = stablecoin.balanceOf(address(this));
+ require(previousBalance >= amount, "Balance insufficient");
+
+ stablecoin.ensureTransfer(address(receiver), amount);
+ require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"), "IERC3156: Callback failed");
+ stablecoin.ensureTransferFrom(address(receiver), address(this), amount + fee);
+ stablecoin.ensureTransfer(s.getTreasury(), protocolFee);
+
+ uint256 finalBalance = stablecoin.balanceOf(address(this));
+ require(finalBalance >= previousBalance + fee, "Access is denied");
+
+ /******************************************************************************************
+ POST
+ ******************************************************************************************/
+
+ delgate().postFlashLoan(msg.sender, key, receiver, token, amount, data);
+
emit FlashLoanBorrowed(address(this), address(receiver), token, amount, fee);
+
return true;
}
+
+ function flashFee(address token, uint256 amount) external view override returns (uint256) {
+ return delgate().getFlashFee(msg.sender, key, token, amount);
+ }
+
+ function maxFlashLoan(address token) external view override returns (uint256) {
+ return delgate().getMaxFlashLoan(msg.sender, key, token);
+ }
}
diff --git a/contracts/core/liquidity/strategies/AaveStrategy.sol b/contracts/core/liquidity/strategies/AaveStrategy.sol
index 68d1c262..8345e27c 100644
--- a/contracts/core/liquidity/strategies/AaveStrategy.sol
+++ b/contracts/core/liquidity/strategies/AaveStrategy.sol
@@ -75,7 +75,7 @@ contract AaveStrategy is ILendingStrategy, Recoverable {
*/
function deposit(bytes32 coverKey, uint256 amount) external override nonReentrant returns (uint256 aTokenReceived) {
s.mustNotBePaused();
- s.callerMustBeProtocolMember();
+ s.senderMustBeProtocolMember();
IVault vault = s.getVault(coverKey);
@@ -121,7 +121,7 @@ contract AaveStrategy is ILendingStrategy, Recoverable {
*/
function withdraw(bytes32 coverKey) external virtual override nonReentrant returns (uint256 stablecoinWithdrawn) {
s.mustNotBePaused();
- s.callerMustBeProtocolMember();
+ s.senderMustBeProtocolMember();
IVault vault = s.getVault(coverKey);
// @suppress-malicious-erc20 `stablecoin`, `aToken` can't be manipulated via user input.
diff --git a/contracts/core/liquidity/strategies/CompoundStrategy.sol b/contracts/core/liquidity/strategies/CompoundStrategy.sol
index 9994391f..fb3d668b 100644
--- a/contracts/core/liquidity/strategies/CompoundStrategy.sol
+++ b/contracts/core/liquidity/strategies/CompoundStrategy.sol
@@ -75,7 +75,7 @@ contract CompoundStrategy is ILendingStrategy, Recoverable {
*/
function deposit(bytes32 coverKey, uint256 amount) external override nonReentrant returns (uint256 cDaiMinted) {
s.mustNotBePaused();
- s.callerMustBeProtocolMember();
+ s.senderMustBeProtocolMember();
IVault vault = s.getVault(coverKey);
@@ -123,7 +123,7 @@ contract CompoundStrategy is ILendingStrategy, Recoverable {
*/
function withdraw(bytes32 coverKey) external virtual override nonReentrant returns (uint256 stablecoinWithdrawn) {
s.mustNotBePaused();
- s.callerMustBeProtocolMember();
+ s.senderMustBeProtocolMember();
IVault vault = s.getVault(coverKey);
// @suppress-malicious-erc20 `stablecoin`, `cDai` can't be manipulated via user input.
diff --git a/contracts/interfaces/ICxTokenFactory.sol b/contracts/interfaces/ICxTokenFactory.sol
index 5bb4f27c..c2cf2fbe 100644
--- a/contracts/interfaces/ICxTokenFactory.sol
+++ b/contracts/interfaces/ICxTokenFactory.sol
@@ -5,7 +5,7 @@ import "./IStore.sol";
import "./IMember.sol";
interface ICxTokenFactory is IMember {
- event CxTokenDeployed(bytes32 indexed key, address cxToken, uint256 expiryDate);
+ event CxTokenDeployed(bytes32 indexed key, address cxToken, uint256 expiryDate, string name, string symbol);
function deploy(
IStore s,
diff --git a/contracts/interfaces/IVault.sol b/contracts/interfaces/IVault.sol
index 9cd0c092..a607a09c 100644
--- a/contracts/interfaces/IVault.sol
+++ b/contracts/interfaces/IVault.sol
@@ -11,11 +11,15 @@ interface IVault is IMember, IERC20 {
event PodsIssued(address indexed account, uint256 issued, uint256 liquidityAdded);
event PodsRedeemed(address indexed account, uint256 redeemed, uint256 liquidityReleased);
event FlashLoanBorrowed(address indexed lender, address indexed borrower, address indexed stablecoin, uint256 amount, uint256 fee);
+ event NPMStaken(address indexed account, uint256 amount);
+ event NPMUnstaken(address indexed account, uint256 amount);
event InterestAccrued(bytes32 indexed key);
+ event Entered(bytes32 indexed key, address indexed account);
+ event Exited(bytes32 indexed key, address indexed account);
function key() external view returns (bytes32);
- function lqt() external view returns (address);
+ function sc() external view returns (address);
/**
* @dev Adds liquidity to the specified cover contract
diff --git a/contracts/interfaces/IVaultDelegate.sol b/contracts/interfaces/IVaultDelegate.sol
new file mode 100644
index 00000000..a2c6f992
--- /dev/null
+++ b/contracts/interfaces/IVaultDelegate.sol
@@ -0,0 +1,131 @@
+// Neptune Mutual Protocol (https://neptunemutual.com)
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity 0.8.0;
+import "./IMember.sol";
+import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
+import "openzeppelin-solidity/contracts/interfaces/IERC3156FlashLender.sol";
+
+interface IVaultDelegate is IMember {
+ function preAddLiquidity(
+ address caller,
+ bytes32 coverKey,
+ uint256 amount,
+ uint256 npmStake
+ ) external returns (uint256 podsToMint, uint256 previousNPMStake);
+
+ function postAddLiquidity(
+ address caller,
+ bytes32 coverKey,
+ uint256 amount,
+ uint256 npmStake
+ ) external;
+
+ function accrueInterestImplementation(address caller, bytes32 coverKey) external;
+
+ function preRemoveLiquidity(
+ address caller,
+ bytes32 coverKey,
+ uint256 amount,
+ uint256 npmStake,
+ bool exit
+ ) external returns (address stablecoin, uint256 stableCoinToRelease);
+
+ function postRemoveLiquidity(
+ address caller,
+ bytes32 coverKey,
+ uint256 amount,
+ uint256 npmStake,
+ bool exit
+ ) external;
+
+ function preTransferGovernance(
+ address caller,
+ bytes32 coverKey,
+ address to,
+ uint256 amount
+ ) external returns (address stablecoin);
+
+ function postTransferGovernance(
+ address caller,
+ bytes32 coverKey,
+ address to,
+ uint256 amount
+ ) external;
+
+ function preTransferToStrategy(
+ address caller,
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external;
+
+ function postTransferToStrategy(
+ address caller,
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external;
+
+ function preReceiveFromStrategy(
+ address caller,
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external;
+
+ function postReceiveFromStrategy(
+ address caller,
+ IERC20 token,
+ bytes32 coverKey,
+ bytes32 strategyName,
+ uint256 amount
+ ) external returns (uint256 income, uint256 loss);
+
+ function preFlashLoan(
+ address caller,
+ bytes32 coverKey,
+ IERC3156FlashBorrower receiver,
+ address token,
+ uint256 amount,
+ bytes calldata data
+ )
+ external
+ returns (
+ IERC20 stablecoin,
+ uint256 fee,
+ uint256 protocolFee
+ );
+
+ function postFlashLoan(
+ address caller,
+ bytes32 coverKey,
+ IERC3156FlashBorrower receiver,
+ address token,
+ uint256 amount,
+ bytes calldata data
+ ) external;
+
+ function calculatePodsImplementation(bytes32 coverKey, uint256 forStablecoinUnits) external view returns (uint256);
+
+ function calculateLiquidityImplementation(bytes32 coverKey, uint256 podsToBurn) external view returns (uint256);
+
+ function getInfoImplementation(bytes32 coverKey, address forAccount) external view returns (uint256[] memory result);
+
+ function getStablecoinBalanceOfImplementation(bytes32 coverKey) external view returns (uint256);
+
+ function getFlashFee(
+ address caller,
+ bytes32 coverKey,
+ address token,
+ uint256 amount
+ ) external view returns (uint256);
+
+ function getMaxFlashLoan(
+ address caller,
+ bytes32 coverKey,
+ address token
+ ) external view returns (uint256);
+}
diff --git a/contracts/libraries/AccessControlLibV1.sol b/contracts/libraries/AccessControlLibV1.sol
index 58f10ff6..ad121e66 100644
--- a/contracts/libraries/AccessControlLibV1.sol
+++ b/contracts/libraries/AccessControlLibV1.sol
@@ -23,70 +23,137 @@ library AccessControlLibV1 {
* @dev Reverts if the sender is not the protocol admin.
*/
function mustBeAdmin(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_ADMIN);
+ _mustHaveAccess(s, NS_ROLES_ADMIN, msg.sender);
}
/**
* @dev Reverts if the sender is not the cover manager.
*/
function mustBeCoverManager(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_COVER_MANAGER);
+ _mustHaveAccess(s, NS_ROLES_COVER_MANAGER, msg.sender);
}
/**
* @dev Reverts if the sender is not the liquidity manager.
*/
function mustBeLiquidityManager(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_LIQUIDITY_MANAGER);
+ _mustHaveAccess(s, NS_ROLES_LIQUIDITY_MANAGER, msg.sender);
}
/**
* @dev Reverts if the sender is not a governance agent.
*/
function mustBeGovernanceAgent(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_GOVERNANCE_AGENT);
+ _mustHaveAccess(s, NS_ROLES_GOVERNANCE_AGENT, msg.sender);
}
/**
* @dev Reverts if the sender is not a governance admin.
*/
function mustBeGovernanceAdmin(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_GOVERNANCE_ADMIN);
+ _mustHaveAccess(s, NS_ROLES_GOVERNANCE_ADMIN, msg.sender);
}
/**
* @dev Reverts if the sender is not an upgrade agent.
*/
function mustBeUpgradeAgent(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_UPGRADE_AGENT);
+ _mustHaveAccess(s, NS_ROLES_UPGRADE_AGENT, msg.sender);
}
/**
* @dev Reverts if the sender is not a recovery agent.
*/
function mustBeRecoveryAgent(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_RECOVERY_AGENT);
+ _mustHaveAccess(s, NS_ROLES_RECOVERY_AGENT, msg.sender);
}
/**
* @dev Reverts if the sender is not the pause agent.
*/
function mustBePauseAgent(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_PAUSE_AGENT);
+ _mustHaveAccess(s, NS_ROLES_PAUSE_AGENT, msg.sender);
}
/**
* @dev Reverts if the sender is not the unpause agent.
*/
function mustBeUnpauseAgent(IStore s) external view {
- _mustHaveAccess(s, NS_ROLES_UNPAUSE_AGENT);
+ _mustHaveAccess(s, NS_ROLES_UNPAUSE_AGENT, msg.sender);
+ }
+
+ /**
+ * @dev Reverts if the sender is not the protocol admin.
+ */
+ function callerMustBeAdmin(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_ADMIN, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not the cover manager.
+ */
+ function callerMustBeCoverManager(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_COVER_MANAGER, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not the liquidity manager.
+ */
+ function callerMustBeLiquidityManager(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_LIQUIDITY_MANAGER, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not a governance agent.
+ */
+ function callerMustBeGovernanceAgent(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_GOVERNANCE_AGENT, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not a governance admin.
+ */
+ function callerMustBeGovernanceAdmin(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_GOVERNANCE_ADMIN, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not an upgrade agent.
+ */
+ function callerMustBeUpgradeAgent(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_UPGRADE_AGENT, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not a recovery agent.
+ */
+ function callerMustBeRecoveryAgent(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_RECOVERY_AGENT, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not the pause agent.
+ */
+ function callerMustBePauseAgent(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_PAUSE_AGENT, caller);
+ }
+
+ /**
+ * @dev Reverts if the sender is not the unpause agent.
+ */
+ function callerMustBeUnpauseAgent(IStore s, address caller) external view {
+ _mustHaveAccess(s, NS_ROLES_UNPAUSE_AGENT, caller);
}
/**
* @dev Reverts if the sender does not have access to the given role.
*/
- function _mustHaveAccess(IStore s, bytes32 role) private view {
- require(hasAccess(s, role, msg.sender), "Forbidden");
+ function _mustHaveAccess(
+ IStore s,
+ bytes32 role,
+ address caller
+ ) private view {
+ require(hasAccess(s, role, caller), "Forbidden");
}
/**
diff --git a/contracts/libraries/ProtoUtilV1.sol b/contracts/libraries/ProtoUtilV1.sol
index a673c6b3..3b5c2959 100644
--- a/contracts/libraries/ProtoUtilV1.sol
+++ b/contracts/libraries/ProtoUtilV1.sol
@@ -35,6 +35,7 @@ library ProtoUtilV1 {
bytes32 public constant CNS_COVER_POLICY_ADMIN = "cns:cover:policy:admin";
bytes32 public constant CNS_COVER_STAKE = "cns:cover:stake";
bytes32 public constant CNS_COVER_VAULT = "cns:cover:vault";
+ bytes32 public constant CNS_COVER_VAULT_DELEGATE = "cns:cover:vault:delegate";
bytes32 public constant CNS_COVER_STABLECOIN = "cns:cover:sc";
bytes32 public constant CNS_COVER_CXTOKEN_FACTORY = "cns:cover:cxtoken:factory";
bytes32 public constant CNS_COVER_VAULT_FACTORY = "cns:cover:vault:factory";
@@ -216,6 +217,7 @@ library ProtoUtilV1 {
bytes32 public constant CNAME_COVER_STAKE = "CoverStake";
bytes32 public constant CNAME_COVER_REASSURANCE = "CoverReassurance";
bytes32 public constant CNAME_LIQUIDITY_VAULT = "Vault";
+ bytes32 public constant CNAME_VAULT_DELEGATE = "VaultDelegate";
bytes32 public constant CNAME_LIQUIDITY_ENGINE = "LiquidityEngine";
bytes32 public constant CNAME_STRATEGY_AAVE = "AaveStrategy";
bytes32 public constant CNAME_STRATEGY_COMPOUND = "CompoundStrategy";
@@ -262,8 +264,20 @@ library ProtoUtilV1 {
* @dev Ensures that the sender matches with the exact contract having the specified name.
* @param name Enter the name of the contract
*/
- function callerMustBeExactContract(IStore s, bytes32 name) external view {
- return mustBeExactContract(s, name, msg.sender);
+ function senderMustBeExactContract(IStore s, bytes32 name) external view {
+ return callerMustBeExactContract(s, name, msg.sender);
+ }
+
+ /**
+ * @dev Ensures that the sender matches with the exact contract having the specified name.
+ * @param name Enter the name of the contract
+ */
+ function callerMustBeExactContract(
+ IStore s,
+ bytes32 name,
+ address caller
+ ) public view {
+ return mustBeExactContract(s, name, caller);
}
function npmToken(IStore s) external view returns (IERC20) {
diff --git a/contracts/libraries/RegistryLibV1.sol b/contracts/libraries/RegistryLibV1.sol
index 03f04c49..31b8e8d2 100644
--- a/contracts/libraries/RegistryLibV1.sol
+++ b/contracts/libraries/RegistryLibV1.sol
@@ -77,6 +77,11 @@ library RegistryLibV1 {
return vault;
}
+ function getVaultDelegate(IStore s) external view returns (address) {
+ address vaultImplementation = getProtocolContract(s, ProtoUtilV1.CNS_COVER_VAULT_DELEGATE);
+ return vaultImplementation;
+ }
+
function getStakingPoolAddress(IStore s) external view returns (address) {
address vault = getProtocolContract(s, ProtoUtilV1.CNS_STAKING_POOL);
return vault;
diff --git a/contracts/libraries/RoutineInvokerLibV1.sol b/contracts/libraries/RoutineInvokerLibV1.sol
index 5daaf355..ece99c5d 100644
--- a/contracts/libraries/RoutineInvokerLibV1.sol
+++ b/contracts/libraries/RoutineInvokerLibV1.sol
@@ -19,6 +19,7 @@ library RoutineInvokerLibV1 {
using StrategyLibV1 for IStore;
using CoverUtilV1 for IStore;
using StoreKeyUtil for IStore;
+ using ValidationLibV1 for IStore;
enum Action {
Deposit,
@@ -41,21 +42,8 @@ library RoutineInvokerLibV1 {
}
}
- function mustBeAccrued(IStore s, bytes32 coverKey) external view {
- require(isAccrualComplete(s, coverKey) == true, "Wait for accrual");
- }
-
- function accrueInterestInternal(IStore s, bytes32 coverKey) external {
- (bool isWithdrawalPeriod, , , , ) = _getWithdrawalInfo(s, coverKey);
- require(isWithdrawalPeriod == true, "Withdrawal hasn't yet begun");
-
- _invokeAssetManagement(s, coverKey);
-
- setAccrualComplete(s, coverKey, true);
- }
-
- function _getWithdrawalInfo(IStore s, bytes32 coverKey)
- private
+ function getWithdrawalInfoInternal(IStore s, bytes32 coverKey)
+ public
view
returns (
bool isWithdrawalPeriod,
@@ -78,7 +66,7 @@ library RoutineInvokerLibV1 {
}
function _executeIsWithdrawalPeriod(IStore s, bytes32 coverKey) private returns (bool) {
- (bool isWithdrawalPeriod, uint256 lendingPeriod, uint256 withdrawalWindow, uint256 start, uint256 end) = _getWithdrawalInfo(s, coverKey);
+ (bool isWithdrawalPeriod, uint256 lendingPeriod, uint256 withdrawalWindow, uint256 start, uint256 end) = getWithdrawalInfoInternal(s, coverKey);
// Without a lending period and withdrawal window, deposit is not possible
if (lendingPeriod == 0 || withdrawalWindow == 0) {
@@ -104,17 +92,17 @@ library RoutineInvokerLibV1 {
s.setUintByKey(getNextWithdrawalStartKey(coverKey), start);
s.setUintByKey(getNextWithdrawalEndKey(coverKey), end);
- setAccrualComplete(s, coverKey, false);
+ setAccrualCompleteInternal(s, coverKey, false);
}
return false;
}
- function isAccrualComplete(IStore s, bytes32 coverKey) public view returns (bool) {
+ function isAccrualCompleteInternal(IStore s, bytes32 coverKey) external view returns (bool) {
return s.getBoolByKey(getAccrualInvocationKey(coverKey));
}
- function setAccrualComplete(
+ function setAccrualCompleteInternal(
IStore s,
bytes32 coverKey,
bool flag
diff --git a/contracts/libraries/StrategyLibV1.sol b/contracts/libraries/StrategyLibV1.sol
index 7c8b90e5..34a181f0 100644
--- a/contracts/libraries/StrategyLibV1.sol
+++ b/contracts/libraries/StrategyLibV1.sol
@@ -10,9 +10,9 @@ import "./ProtoUtilV1.sol";
import "./NTransferUtilV2.sol";
library StrategyLibV1 {
+ using NTransferUtilV2 for IERC20;
using ProtoUtilV1 for IStore;
using StoreKeyUtil for IStore;
- using NTransferUtilV2 for IERC20;
event StrategyAdded(address indexed strategy);
@@ -136,15 +136,13 @@ library StrategyLibV1 {
return s.getUintByKey(k);
}
- function transferToStrategyInternal(
+ function preTransferToStrategyInternal(
IStore s,
IERC20 token,
bytes32 coverKey,
bytes32 strategyName,
uint256 amount
) external {
- // @suppress-malicious-erc20 @note: token should be checked on the calling contract
- token.ensureTransfer(msg.sender, amount);
bool isStablecoin = s.getStablecoin() == address(token) ? true : false;
if (isStablecoin == false) {
@@ -155,15 +153,13 @@ library StrategyLibV1 {
_addToSpecificStrategyOut(s, coverKey, strategyName, address(token), amount);
}
- function receiveFromStrategyInternal(
+ function postReceiveFromStrategyInternal(
IStore s,
IERC20 token,
bytes32 coverKey,
bytes32 strategyName,
uint256 toReceive
) external returns (uint256 income, uint256 loss) {
- // @suppress-malicious-erc20 token should be checked on the calling contract
- token.ensureTransferFrom(msg.sender, address(this), toReceive);
bool isStablecoin = s.getStablecoin() == address(token) ? true : false;
if (isStablecoin == false) {
diff --git a/contracts/libraries/ValidationLibV1.sol b/contracts/libraries/ValidationLibV1.sol
index 7b86c3d0..992e5c9f 100644
--- a/contracts/libraries/ValidationLibV1.sol
+++ b/contracts/libraries/ValidationLibV1.sol
@@ -93,39 +93,58 @@ library ValidationLibV1 {
require(isCoverOwner || isCoverContract, "Forbidden");
}
- function callerMustBeCoverOwnerOrAdmin(IStore s, bytes32 key) external view {
+ function senderMustBeCoverOwnerOrAdmin(IStore s, bytes32 key) external view {
if (AccessControlLibV1.hasAccess(s, AccessControlLibV1.NS_ROLES_ADMIN, msg.sender) == false) {
mustBeCoverOwner(s, key, msg.sender);
}
}
- function callerMustBePolicyContract(IStore s) external view {
- s.callerMustBeExactContract(ProtoUtilV1.CNS_COVER_POLICY);
+ function senderMustBePolicyContract(IStore s) external view {
+ s.senderMustBeExactContract(ProtoUtilV1.CNS_COVER_POLICY);
}
- function callerMustBePolicyManagerContract(IStore s) external view {
- s.callerMustBeExactContract(ProtoUtilV1.CNS_COVER_POLICY_MANAGER);
+ function senderMustBePolicyManagerContract(IStore s) external view {
+ s.senderMustBeExactContract(ProtoUtilV1.CNS_COVER_POLICY_MANAGER);
}
- function callerMustBeCoverContract(IStore s) external view {
- s.callerMustBeExactContract(ProtoUtilV1.CNS_COVER);
+ function senderMustBeCoverContract(IStore s) external view {
+ s.senderMustBeExactContract(ProtoUtilV1.CNS_COVER);
}
- function callerMustBeVaultContract(IStore s, bytes32 key) external view {
+ function senderMustBeVaultContract(IStore s, bytes32 key) external view {
address vault = s.getVaultAddress(key);
require(msg.sender == vault, "Forbidden");
}
- function callerMustBeGovernanceContract(IStore s) external view {
- s.callerMustBeExactContract(ProtoUtilV1.CNS_GOVERNANCE);
+ function senderMustBeGovernanceContract(IStore s) external view {
+ s.senderMustBeExactContract(ProtoUtilV1.CNS_GOVERNANCE);
}
- function callerMustBeClaimsProcessorContract(IStore s) external view {
- s.callerMustBeExactContract(ProtoUtilV1.CNS_CLAIM_PROCESSOR);
+ function senderMustBeClaimsProcessorContract(IStore s) external view {
+ s.senderMustBeExactContract(ProtoUtilV1.CNS_CLAIM_PROCESSOR);
}
- function callerMustBeStrategyContract(IStore s) external view {
- bool callerIsStrategyContract = s.getBoolByKey(_getIsActiveStrategyKey(msg.sender));
+ function callerMustBeClaimsProcessorContract(IStore s, address caller) external view {
+ s.callerMustBeExactContract(ProtoUtilV1.CNS_CLAIM_PROCESSOR, caller);
+ }
+
+ function senderMustBeStrategyContract(IStore s) external view {
+ bool senderIsStrategyContract = s.getBoolByKey(_getIsActiveStrategyKey(msg.sender));
+ require(senderIsStrategyContract == true, "Not a strategy contract");
+ }
+
+ function callerMustBeStrategyContract(IStore s, address caller) external view {
+ bool callerIsStrategyContract = s.getBoolByKey(_getIsActiveStrategyKey(caller));
+ require(callerIsStrategyContract == true, "Not a strategy contract");
+ }
+
+ function callerMustBeSpecificStrategyContract(
+ IStore s,
+ address caller,
+ bytes32 /*strategyName*/
+ ) external view {
+ // @todo
+ bool callerIsStrategyContract = s.getBoolByKey(_getIsActiveStrategyKey(caller));
require(callerIsStrategyContract == true, "Not a strategy contract");
}
@@ -133,7 +152,7 @@ library ValidationLibV1 {
return keccak256(abi.encodePacked(ProtoUtilV1.NS_LENDING_STRATEGY_ACTIVE, strategyAddress));
}
- function callerMustBeProtocolMember(IStore s) external view {
+ function senderMustBeProtocolMember(IStore s) external view {
require(s.isProtocolMember(msg.sender), "Forbidden");
}
diff --git a/contracts/libraries/VaultLibV1.sol b/contracts/libraries/VaultLibV1.sol
index 367df54b..355cfab6 100644
--- a/contracts/libraries/VaultLibV1.sol
+++ b/contracts/libraries/VaultLibV1.sol
@@ -4,7 +4,6 @@
pragma solidity 0.8.0;
import "../interfaces/IStore.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
-import "./NTransferUtilV2.sol";
import "./ProtoUtilV1.sol";
import "./PolicyHelperV1.sol";
import "./StoreKeyUtil.sol";
@@ -16,7 +15,6 @@ import "./StrategyLibV1.sol";
import "openzeppelin-solidity/contracts/interfaces/IERC3156FlashLender.sol";
library VaultLibV1 {
- using NTransferUtilV2 for IERC20;
using ProtoUtilV1 for IStore;
using PolicyHelperV1 for IStore;
using StoreKeyUtil for IStore;
@@ -38,7 +36,9 @@ library VaultLibV1 {
uint256 balance = getStablecoinBalanceOfInternal(s, coverKey);
uint256 podSupply = IERC20(pod).totalSupply();
- // This smart contract contains stablecoins without liquidity provider contribution
+ // This smart contract contains stablecoins without liquidity provider contribution.
+ // This can happen if someone wants to create a nuisance by sending stablecoin
+ // to this contract immediately after deployment.
if (podSupply == 0 && balance > 0) {
revert("Liquidity/POD mismatch");
}
@@ -64,12 +64,11 @@ library VaultLibV1 {
IStore s,
bytes32 coverKey,
address pod,
- address stablecoin,
uint256 podsToBurn
) public view returns (uint256) {
require(s.getBoolByKeys(ProtoUtilV1.NS_COVER_HAS_FLASH_LOAN, coverKey) == false, "On flash loan, please try again");
- uint256 contractStablecoinBalance = IERC20(stablecoin).balanceOf(address(this));
+ uint256 contractStablecoinBalance = IERC20(s.getStablecoin()).balanceOf(s.getVaultAddress(coverKey));
return (contractStablecoinBalance * podsToBurn) / IERC20(pod).totalSupply();
}
@@ -78,7 +77,6 @@ library VaultLibV1 {
* @param s Provide a store instance
* @param coverKey Specify cover key to obtain the info of.
* @param pod Provide the address of the POD
- * @param stablecoin Provide the address of the Vault stablecoin
* @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
@@ -95,19 +93,18 @@ library VaultLibV1 {
IStore s,
bytes32 coverKey,
address pod,
- address stablecoin,
address you
) external view returns (uint256[] memory values) {
values = new uint256[](11);
values[0] = IERC20(pod).totalSupply(); // Total PODs in existence
values[1] = getStablecoinBalanceOfInternal(s, coverKey);
- values[2] = s.getAmountInStrategies(coverKey, stablecoin); // Stablecoins lent outside of the protocol
+ values[2] = s.getAmountInStrategies(coverKey, s.getStablecoin()); // Stablecoins lent outside of the protocol
values[3] = s.getReassuranceAmountInternal(coverKey); // Total reassurance for this cover
values[4] = IERC20(pod).balanceOf(you); // Your POD Balance
values[5] = _getCoverLiquidityAddedInternal(s, coverKey, you); // Sum of your deposits (in stablecoin)
values[6] = _getCoverLiquidityRemovedInternal(s, coverKey, you); // Sum of your withdrawals (in stablecoin)
- values[7] = calculateLiquidityInternal(s, coverKey, pod, stablecoin, values[5]); // My share of the liquidity pool (in stablecoin)
+ values[7] = calculateLiquidityInternal(s, coverKey, pod, values[5]); // My share of the liquidity pool (in stablecoin)
values[8] = s.getUintByKey(RoutineInvokerLibV1.getNextWithdrawalStartKey(coverKey));
values[9] = s.getUintByKey(RoutineInvokerLibV1.getNextWithdrawalEndKey(coverKey));
}
@@ -129,36 +126,32 @@ library VaultLibV1 {
}
/**
- * @dev Adds liquidity to the specified cover contract
+ * @dev Called before adding liquidity to the specified cover contract
* @param coverKey Enter the cover key
* @param account Specify the account on behalf of which the liquidity is being added.
* @param amount Enter the amount of liquidity token to supply.
* @param npmStakeToAdd Enter the amount of NPM token to stake.
*/
- function addLiquidityInternal(
+ function preAddLiquidityInternal(
IStore s,
bytes32 coverKey,
address pod,
- address stablecoin,
address account,
uint256 amount,
uint256 npmStakeToAdd
- ) external returns (uint256) {
+ ) external returns (uint256 podsToMint, uint256 myPreviousStake) {
+ s.mustNotBePaused();
+ s.mustHaveNormalCoverStatus(coverKey);
+
// @suppress-address-trust-issue, @suppress-malicious-erc20 The address `stablecoin` can be trusted here because we are ensuring it matches with the protocol stablecoin address.
// @suppress-address-trust-issue The address `account` can be trusted here because we are not treating it as a contract (even it were).
require(account != address(0), "Invalid account");
- require(stablecoin == s.getStablecoin(), "Vault migration required");
// Update values
- _updateNpmStake(s, coverKey, account, npmStakeToAdd);
+ myPreviousStake = _updateNpmStake(s, coverKey, account, npmStakeToAdd);
_updateCoverLiquidity(s, coverKey, account, amount);
- uint256 podsToMint = calculatePodsInternal(s, coverKey, pod, amount);
-
- IERC20(stablecoin).ensureTransferFrom(account, address(this), amount);
- IERC20(s.getNpmTokenAddress()).ensureTransferFrom(account, address(this), npmStakeToAdd);
-
- return podsToMint;
+ podsToMint = calculatePodsInternal(s, coverKey, pod, amount);
}
function _updateNpmStake(
@@ -166,8 +159,8 @@ library VaultLibV1 {
bytes32 coverKey,
address account,
uint256 amount
- ) private {
- uint256 myPreviousStake = _getMyNpmStake(s, coverKey, account);
+ ) private returns (uint256 myPreviousStake) {
+ myPreviousStake = _getMyNpmStake(s, coverKey, account);
require(amount + myPreviousStake >= s.getMinStakeToAddLiquidity(), "Insufficient stake");
if (amount > 0) {
@@ -203,36 +196,48 @@ library VaultLibV1 {
myStake = s.getUintByKey(CoverUtilV1.getCoverLiquidityStakeIndividualKey(coverKey, account));
}
+ function mustHaveNoBalanceInStrategies(
+ IStore s,
+ bytes32 coverKey,
+ address stablecoin
+ ) public view {
+ require(s.getAmountInStrategies(coverKey, stablecoin) == 0, "Strategy balance is not zero");
+ }
+
/**
* @dev Removes liquidity from the specified cover contract
* @param coverKey Enter the cover key
* @param podsToRedeem Enter the amount of liquidity token to remove.
*/
- function removeLiquidityInternal(
+ function preRemoveLiquidityInternal(
IStore s,
bytes32 coverKey,
address pod,
uint256 podsToRedeem,
uint256 npmStakeToRemove,
bool exit
- ) external returns (uint256) {
+ ) external returns (address stablecoin, uint256 releaseAmount) {
+ s.mustNotBePaused();
+ mustBeAccrued(s, coverKey);
+
+ require(podsToRedeem > 0, "Please specify amount");
+
+ stablecoin = s.getStablecoin();
+
// @suppress-address-trust-issue, @suppress-malicious-erc20 The address `pod` although can only
// come from VaultBase, we still need to ensure if it is a protocol member.
// Check `_redeemPods` for more info.
s.mustHaveNormalCoverStatus(coverKey);
s.mustBeDuringWithdrawalPeriod(coverKey);
+ mustHaveNoBalanceInStrategies(s, coverKey, stablecoin);
// Redeem the PODs and receive DAI
- uint256 releaseAmount = _redeemPods(s, coverKey, pod, podsToRedeem);
+ releaseAmount = _redeemPods(s, coverKey, pod, podsToRedeem);
// Unstake NPM tokens
if (npmStakeToRemove > 0) {
_unStakeNpm(s, coverKey, npmStakeToRemove, exit);
- IERC20(s.getNpmTokenAddress()).ensureTransfer(msg.sender, npmStakeToRemove);
}
-
- s.updateStateAndLiquidity(coverKey);
- return releaseAmount;
}
function _unStakeNpm(
@@ -261,10 +266,9 @@ library VaultLibV1 {
}
s.mustBeProtocolMember(pod);
- address stablecoin = s.getStablecoin();
uint256 available = s.getStablecoinBalanceOfCoverPoolInternal(coverKey);
- uint256 releaseAmount = calculateLiquidityInternal(s, coverKey, pod, stablecoin, podsToRedeem);
+ uint256 releaseAmount = calculateLiquidityInternal(s, coverKey, pod, podsToRedeem);
// You may need to wait till active policies expire
require(available >= releaseAmount, "Insufficient balance, wait till policy expiry."); // solhint-disable-line
@@ -273,9 +277,6 @@ library VaultLibV1 {
s.subtractUintByKey(CoverUtilV1.getCoverLiquidityKey(coverKey), releaseAmount);
s.addUintByKeys(ProtoUtilV1.NS_COVER_LIQUIDITY_REMOVED, coverKey, msg.sender, releaseAmount);
- IERC20(pod).ensureTransferFrom(msg.sender, address(this), podsToRedeem);
- IERC20(stablecoin).ensureTransfer(msg.sender, releaseAmount);
-
return releaseAmount;
}
@@ -283,10 +284,25 @@ library VaultLibV1 {
return string(abi.encodePacked(string(abi.encodePacked(coverKey)), "-pod"));
}
+ function accrueInterestInternal(IStore s, bytes32 coverKey) external {
+ s.mustNotBePaused();
+
+ (bool isWithdrawalPeriod, , , , ) = s.getWithdrawalInfoInternal(coverKey);
+ require(isWithdrawalPeriod == true, "Withdrawal hasn't yet begun");
+
+ s.updateStateAndLiquidity(coverKey);
+
+ s.setAccrualCompleteInternal(coverKey, true);
+ }
+
+ function mustBeAccrued(IStore s, bytes32 coverKey) public view {
+ require(s.isAccrualCompleteInternal(coverKey) == true, "Wait for accrual");
+ }
+
function getStablecoinBalanceOfInternal(IStore s, bytes32 coverKey) public view returns (uint256) {
address stablecoin = s.getStablecoin();
- uint256 balance = IERC20(stablecoin).balanceOf(address(this));
+ uint256 balance = IERC20(stablecoin).balanceOf(s.getVaultAddress(coverKey));
uint256 inStrategies = s.getAmountInStrategies(coverKey, stablecoin);
return balance + inStrategies;
@@ -302,6 +318,7 @@ library VaultLibV1 {
*/
function getFlashFeesInternal(
IStore s,
+ bytes32 coverKey,
address token,
uint256 amount
) public view returns (uint256 fee, uint256 protocolFee) {
@@ -315,6 +332,7 @@ library VaultLibV1 {
If the token is not supported flashFee MUST revert.
*/
require(stablecoin == token, "Unsupported token");
+ require(IERC20(stablecoin).balanceOf(s.getVaultAddress(coverKey)) > amount, "Amount insufficient");
uint256 rate = _getFlashLoanFeeRateInternal(s);
uint256 protocolRate = _getProtocolFlashLoanFeeRateInternal(s);
@@ -325,10 +343,11 @@ library VaultLibV1 {
function getFlashFeeInternal(
IStore s,
+ bytes32 coverKey,
address token,
uint256 amount
) external view returns (uint256) {
- (uint256 fee, ) = getFlashFeesInternal(s, token, amount);
+ (uint256 fee, ) = getFlashFeesInternal(s, coverKey, token, amount);
return fee;
}
@@ -345,12 +364,16 @@ library VaultLibV1 {
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
- function getMaxFlashLoanInternal(IStore s, address token) external view returns (uint256) {
+ function getMaxFlashLoanInternal(
+ IStore s,
+ bytes32 coverKey,
+ address token
+ ) external view returns (uint256) {
address stablecoin = s.getStablecoin();
require(stablecoin != address(0), "Cover liquidity uninitialized");
if (stablecoin == token) {
- return IERC20(stablecoin).balanceOf(address(this));
+ return IERC20(stablecoin).balanceOf(s.getVaultAddress(coverKey));
}
/*
@@ -376,31 +399,5 @@ library VaultLibV1 {
address token,
uint256 amount,
bytes calldata data
- ) external returns (uint256) {
- // @suppress-address-trust-issue, @suppress-malicious-erc20 `stablecoin` can't be manipulated via user input.
- IERC20 stablecoin = IERC20(s.getStablecoin());
- (uint256 fee, uint256 protocolFee) = getFlashFeesInternal(s, token, amount);
- uint256 previousBalance = stablecoin.balanceOf(address(this));
-
- require(address(stablecoin) == token, "Unknown token");
- require(amount > 0, "Loan too small");
- require(fee > 0, "Fee too little");
- require(previousBalance >= amount, "Balance insufficient");
-
- s.setBoolByKeys(ProtoUtilV1.NS_COVER_HAS_FLASH_LOAN, key, true);
-
- stablecoin.ensureTransfer(address(receiver), amount);
- require(receiver.onFlashLoan(msg.sender, token, amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"), "IERC3156: Callback failed");
- stablecoin.ensureTransferFrom(address(receiver), address(this), amount + fee);
- stablecoin.ensureTransfer(s.getTreasury(), protocolFee);
-
- uint256 finalBalance = stablecoin.balanceOf(address(this));
- require(finalBalance >= previousBalance + fee, "Access is denied");
-
- s.setBoolByKeys(ProtoUtilV1.NS_COVER_HAS_FLASH_LOAN, key, false);
-
- s.updateStateAndLiquidity(key);
-
- return fee;
- }
+ ) external returns (uint256) {}
}
diff --git a/examples/covers.js b/examples/covers.js
index 7586293d..240f17bb 100644
--- a/examples/covers.js
+++ b/examples/covers.js
@@ -71,7 +71,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'BEC' },
stakingTarget: ether(400_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.000000093),
+ rewardPerBlock: 4_566_764_500,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(15_000_000)
},
@@ -80,7 +80,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'BEC' },
stakingTarget: ether(400_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000003),
+ rewardPerBlock: 4_566_764_500,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(30_000)
},
@@ -89,8 +89,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'BEC' },
stakingTarget: ether(400_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000034),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 4_566_764_500,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 60),
rewardTokenDeposit: ether(18_000_000)
}
}
@@ -139,7 +139,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'CRPOOL' },
stakingTarget: ether(10_000_000),
maxStake: ether(90_000),
- rewardPerBlock: ether(0.0000080),
+ rewardPerBlock: 3_300_000_000,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(27_500_000)
},
@@ -148,7 +148,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'CRPOOL' },
stakingTarget: ether(10_000_000),
maxStake: ether(90_000),
- rewardPerBlock: ether(0.0000080),
+ rewardPerBlock: 3_300_000_000,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(200_000)
},
@@ -157,8 +157,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'CRPOOL' },
stakingTarget: ether(10_000_000),
maxStake: ether(90_000),
- rewardPerBlock: ether(0.0000080),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 3_300_000_000,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 150),
rewardTokenDeposit: ether(27_500_000)
}
}
@@ -238,7 +238,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'HWT' },
stakingTarget: ether(1_000_000),
maxStake: ether(25_000),
- rewardPerBlock: ether(0.0000025),
+ rewardPerBlock: 2_534_320_000,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(15_000_000)
},
@@ -247,7 +247,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'HWT' },
stakingTarget: ether(1_000_000),
maxStake: ether(25_000),
- rewardPerBlock: ether(0.0000025),
+ rewardPerBlock: 2_534_320_000,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(25_000)
},
@@ -256,8 +256,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'HWT' },
stakingTarget: ether(1_000_000),
maxStake: ether(25_000),
- rewardPerBlock: ether(0.0000025),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 2_534_320_000,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 360),
rewardTokenDeposit: ether(15_000_000)
}
}
@@ -307,7 +307,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'OBK' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.000000093),
+ rewardPerBlock: 4_400_000_000,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(35_000_000)
},
@@ -316,7 +316,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'OBK' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000003),
+ rewardPerBlock: 4_400_000_000,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(30_000)
},
@@ -325,8 +325,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'OBK' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000034),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 4_400_000_000,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 120),
rewardTokenDeposit: ether(35_000_000)
}
}
@@ -398,7 +398,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'SABRE' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.000000093),
+ rewardPerBlock: 2_948_440_000,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(35_000_000)
},
@@ -407,7 +407,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'SABRE' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000003),
+ rewardPerBlock: 2_948_440_000,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(30_000)
},
@@ -416,8 +416,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'SABRE' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000034),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 2_948_440_000,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 180),
rewardTokenDeposit: ether(35_000_000)
}
}
@@ -462,7 +462,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'XD' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.000000093),
+ rewardPerBlock: 6_400_000_000,
lockupPeriodInBlocks: minutesToBlocks(3, 5),
rewardTokenDeposit: ether(35_000_000)
},
@@ -471,7 +471,7 @@ const covers = [
uniRewardTokenDollarPair: { token: 'XD' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000003),
+ rewardPerBlock: 6_400_000_000,
lockupPeriodInBlocks: minutesToBlocks(31337, 5),
rewardTokenDeposit: ether(30_000)
},
@@ -480,8 +480,8 @@ const covers = [
uniRewardTokenDollarPair: { token: 'XD' },
stakingTarget: ether(800_000),
maxStake: ether(20_000),
- rewardPerBlock: ether(0.00000034),
- lockupPeriodInBlocks: minutesToBlocks(42, 5),
+ rewardPerBlock: 6_400_000_000,
+ lockupPeriodInBlocks: minutesToBlocks(80001, 60),
rewardTokenDeposit: ether(35_000_000)
}
}
diff --git a/hardhat.config.js b/hardhat.config.js
index c7757a82..ef7ea9bb 100644
--- a/hardhat.config.js
+++ b/hardhat.config.js
@@ -24,13 +24,6 @@ const config = {
gasPrice: 15000000000,
gas: 'auto'
},
- kovan: {
- url: `${process.env.KOVAN_RPC_URL}`,
- chainId: 42,
- accounts: [process.env.PRIVATE_KEY],
- gasPrice: 7000000000,
- gas: 'auto'
- },
bscTestnet: {
url: 'https://data-seed-prebsc-1-s1.binance.org:8545/',
chainId: 97,
@@ -79,7 +72,7 @@ const config = {
disambiguatePaths: false
},
etherscan: {
- apiKey: process.env.ETHERSCAN_API_KEY,
+ apiKey: process.env.POLYGONSCAN_API_KEY,
apiKeyAll: {
mainnet: process.env.ETHERSCAN_API_KEY,
ropsten: process.env.ETHERSCAN_API_KEY,
diff --git a/scripts/config/network/index.js b/scripts/config/network/index.js
index 25f9760b..4b6d0a52 100644
--- a/scripts/config/network/index.js
+++ b/scripts/config/network/index.js
@@ -1,13 +1,11 @@
const hardhat = require('./hardhat')
const mainnet = require('./mainnet')
const ropsten = require('./ropsten')
-const kovan = require('./kovan')
const mumbai = require('./mumbai')
module.exports = {
...hardhat,
...ropsten,
- ...kovan,
...mumbai,
...mainnet
}
diff --git a/scripts/config/network/kovan.js b/scripts/config/network/kovan.js
deleted file mode 100644
index 5d022717..00000000
--- a/scripts/config/network/kovan.js
+++ /dev/null
@@ -1,89 +0,0 @@
-require('dotenv').config()
-const wallet = require('../../../util/wallet')
-const { ACCESS_CONTROL } = require('../../../util/key')
-const MINUTES = 60
-
-const config = {
- 42: {
- network: 'Kovan Test Network',
- chainId: 3,
- pool: {
- bond: {
- period: 10 * MINUTES
- }
- },
- cover: {
- lendingPeriod: 60 * MINUTES,
- withdrawalWindow: 60 * MINUTES,
- claimPeriod: 120 * MINUTES,
- cooldownPeriod: 5 * MINUTES
- },
- knownAccounts: [
- {
- account: wallet.toWalletAddress(process.env.PRIVATE_KEY),
- roles: [
- ACCESS_CONTROL.ADMIN,
- ACCESS_CONTROL.COVER_MANAGER,
- ACCESS_CONTROL.LIQUIDITY_MANAGER,
- ACCESS_CONTROL.GOVERNANCE_ADMIN,
- ACCESS_CONTROL.RECOVERY_AGENT,
- ACCESS_CONTROL.UNPAUSE_AGENT,
- ACCESS_CONTROL.UPGRADE_AGENT
- ]
- }, {
- account: '0xA96813969437F3bad7B59335934aa75933670615',
- roles: [
- ACCESS_CONTROL.ADMIN,
- ACCESS_CONTROL.COVER_MANAGER,
- ACCESS_CONTROL.LIQUIDITY_MANAGER,
- ACCESS_CONTROL.GOVERNANCE_ADMIN,
- ACCESS_CONTROL.RECOVERY_AGENT,
- ACCESS_CONTROL.UNPAUSE_AGENT,
- ACCESS_CONTROL.UPGRADE_AGENT
- ]
- }
- ],
- deployedTokens: {
- DAI: '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', // Aave Kovan DAI
- NPM: '0x481B55f34Ef7839c408f35f6B57a68cd54B84eFC',
- CRPOOL: '0xbe29A48292218ca80041F03a946a153207EFC868',
- HWT: '0x087BeA6C46A8F6efe94809cC8074fb69445c684e',
- OBK: '0x211C1261d25BE74126728642D2DebD37A611B26E',
- SABRE: '0x52471FFA579f8278fA23eF153c7ac48498Fe772b',
- BEC: '0x8C155c9D52C2Fd2DFCCa2C26e833C1a6eabDbb6d',
- XD: '0xE7559d514EC2d6E1883Dd53DFF4bC108d457f1b6',
- aToken: '0xdcf0af9e59c002fa3aa091a46196b37530fd48a8',
- cDai: '0xF49eBE5A0d62cc8f0318AD14620D17dcc2D53935'
- },
- stablecoinPairs: {
- NPM_DAI: '0x058A0445B488Cba8cab4963181eE7ec3da71e7A0',
- CRPOOL_DAI: '0x4A79e880088687b9b014a15e1a0eec1148bB66C7',
- HWT_DAI: '0xe6fEb3F713B5a40903FF2aad66383059faaa51D8',
- OBK_DAI: '0xaC1e24504432004ea2a8c92Ab4221A002bd2AF1c',
- SABRE_DAI: '0xf9EbDA687bFaCa47c2Ba84417324E27c4ce23662',
- BEC_DAI: '0xAd11864feCf8a2b8b2260c89771d0419a527B532',
- XD_DAI: '0x3A37460ec2675c6A90Dcd00c38Da04a764F684f9'
- },
- uniswapV2Like: {
- description: 'Sushiswap on Kovan',
- addresses: {
- factory: '0xc35DADB65012eC5796536bD9864eD8773aBc74C4',
- router: '0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506',
- masterChef: '0x80C7DD17B01855a6D2347444a0FCC36136a314de'
- }
- },
- aave: {
- description: 'Aave V2 on Kovan',
- addresses: {
- lendingPool: '0xE0fBa4Fc209b4948668006B2bE61711b7f465bAe'
- }
- },
- compound: {
- dai: {
- delegator: '0xF49eBE5A0d62cc8f0318AD14620D17dcc2D53935'
- }
- }
- }
-}
-
-module.exports = config
diff --git a/scripts/config/network/mumbai.js b/scripts/config/network/mumbai.js
index d3b290d4..6562610b 100644
--- a/scripts/config/network/mumbai.js
+++ b/scripts/config/network/mumbai.js
@@ -73,7 +73,7 @@ const config = {
}
},
aave: {
- description: 'Aave V2 on Kovan',
+ description: 'Aave V2 on Mumbai',
addresses: {
lendingPool: null
}
diff --git a/stories/0. setup.story.js b/stories/0. setup.story.js
index e1d4d600..a465a4f9 100644
--- a/stories/0. setup.story.js
+++ b/stories/0. setup.story.js
@@ -121,6 +121,14 @@ describe('Protocol Initialization Stories', () => {
previous.daiBalance = expected
})
+ it('correctness rule: pods should match the number of tokens deposited', async () => {
+ const pod = await composer.vault.getVault(contracts, coverKey)
+ const [owner] = await ethers.getSigners()
+
+ const pods = await pod.balanceOf(owner.address)
+ pods.toString().should.equal(previous.daiBalance.toString())
+ })
+
it('corretness rule: reassurance token should\'ve been correctly transferred to the reassurance vault', async () => {
const vault = await storeUtil.getReassuranceVaultAddress(contracts.store)
@@ -152,12 +160,12 @@ describe('Protocol Initialization Stories', () => {
previous.daiBalance = expected
})
- it('correctness rule: pods should match the number of tokens deposited', async () => {
+ it('correctness rule: pods should be less than the number of tokens deposited', async () => {
const pod = await composer.vault.getVault(contracts, coverKey)
const [owner] = await ethers.getSigners()
const pods = await pod.balanceOf(owner.address)
- pods.toString().should.equal(previous.daiBalance.toString())
+ pods.should.be.lte(previous.daiBalance.toString())
})
it('reassurance token allocation was increased', async () => {
diff --git a/stories/4.liquidity.story.js b/stories/4.liquidity.story.js
index 4ea92064..2124e673 100644
--- a/stories/4.liquidity.story.js
+++ b/stories/4.liquidity.story.js
@@ -59,13 +59,28 @@ describe('Liquidity Stories', () => {
await vault.addLiquidity(coverKey, initialLiquidity, minReportingStake)
})
- it('deployer removed $3M from Bitmart cover pool', async () => {
+ it('interest could not be accrued before withdrawal period', async () => {
+ const vault = await composer.vault.getVault(contracts, coverKey)
+
+ await vault.accrueInterest().should.be.rejectedWith('Withdrawal hasn\'t yet begun')
+ })
+
+ it('deployer can not remove liquidity without interest accrual', async () => {
const [owner] = await ethers.getSigners()
const toRedeem = helper.ether(3_000_000)
const vault = await composer.vault.getVault(contracts, coverKey)
await network.provider.send('evm_increaseTime', [181 * DAYS])
+ await approve(vault.address, vault.address, owner)
+ await vault.removeLiquidity(coverKey, toRedeem, '0', false).should.be.rejectedWith('Wait for accrual')
+ })
+
+ it('deployer removed $3M from Bitmart cover pool', async () => {
+ const [owner] = await ethers.getSigners()
+ const toRedeem = helper.ether(3_000_000)
+ const vault = await composer.vault.getVault(contracts, coverKey)
+
await vault.accrueInterest()
await approve(vault.address, vault.address, owner)
diff --git a/test/claims-processor/deps.js b/test/claims-processor/deps.js
index 0c5037e0..35a0b88d 100644
--- a/test/claims-processor/deps.js
+++ b/test/claims-processor/deps.js
@@ -13,7 +13,7 @@ const deployDependencies = async () => {
AccessControlLibV1: all.accessControlLibV1.address,
BaseLibV1: all.baseLibV1.address,
GovernanceUtilV1: all.governanceLib.address,
- RoutineInvokerLibV1: all.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: all.routineInvokerLibV1.address,
NTransferUtilV2: all.transferLib.address,
ProtoUtilV1: all.protoUtilV1.address,
RegistryLibV1: all.registryLibV1.address,
diff --git a/test/protocol.spec.js b/test/protocol.spec.js
index f9637de8..1d53d269 100644
--- a/test/protocol.spec.js
+++ b/test/protocol.spec.js
@@ -36,15 +36,12 @@ const deployDependencies = async () => {
StoreKeyUtil: storeKeyUtil.address
})
- const transferLib = await deployer.deploy(cache, 'NTransferUtilV2')
-
const strategyLibV1 = await deployer.deployWithLibraries(cache, 'StrategyLibV1', {
- NTransferUtilV2: transferLib.address,
ProtoUtilV1: protoUtilV1.address,
StoreKeyUtil: storeKeyUtil.address
})
- const RoutineInvokerLibV1 = await deployer.deployWithLibraries(cache, 'RoutineInvokerLibV1', {
+ const routineInvokerLibV1 = await deployer.deployWithLibraries(cache, 'RoutineInvokerLibV1', {
CoverUtilV1: coverUtilV1.address,
ProtoUtilV1: protoUtilV1.address,
RegistryLibV1: registryLibV1.address,
@@ -54,7 +51,7 @@ const deployDependencies = async () => {
const governanceUtilV1 = await deployer.deployWithLibraries(cache, 'GovernanceUtilV1', {
CoverUtilV1: coverUtilV1.address,
- RoutineInvokerLibV1: RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: routineInvokerLibV1.address,
StoreKeyUtil: storeKeyUtil.address
})
diff --git a/util/composer/add-pools.js b/util/composer/add-pools.js
index 461a20ba..3888d9cd 100644
--- a/util/composer/add-pools.js
+++ b/util/composer/add-pools.js
@@ -9,7 +9,7 @@ const addPodStaking = async (intermediate, cache, info, contracts, provider) =>
await approve(rewardToken, stakingPoolContract.address, provider)
const addresses = [stakingToken, uniStakingTokenDollarPair, rewardToken, uniRewardTokenDollarPair]
- const values = [stakingTarget, maxStake, platformFee, rewardPerBlock, lockupPeriodInBlocks, rewardTokenDeposit]
+ const values = [stakingTarget, maxStake, platformFee, rewardPerBlock.toString(), lockupPeriodInBlocks, rewardTokenDeposit]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key, name, poolType, addresses, values)
}
diff --git a/util/composer/covers.js b/util/composer/covers.js
index deb65bcf..733af0a3 100644
--- a/util/composer/covers.js
+++ b/util/composer/covers.js
@@ -1,6 +1,7 @@
const { ethers } = require('ethers')
const { covers } = require('../../examples/covers')
const ipfs = require('../ipfs')
+const rest = (time) => new Promise((resolve) => setTimeout(resolve, time))
const createCovers = async (payload) => {
const { intermediate, cache, contracts } = payload
@@ -13,6 +14,7 @@ const createCovers = async (payload) => {
for (const i in covers) {
const info = covers[i]
await create(payload, info)
+ await rest(200)
}
}
@@ -36,6 +38,7 @@ const create = async (payload, info) => {
]
await intermediate(cache, cover, 'addCover', key, hashBytes32, dai.address, requiresWhitelist, values)
+ await rest(100)
await intermediate(cache, cover, 'deployVault', key)
}
diff --git a/util/composer/initializer.js b/util/composer/initializer.js
index 6d8af22e..0ef37c59 100644
--- a/util/composer/initializer.js
+++ b/util/composer/initializer.js
@@ -14,7 +14,7 @@ const { getExternalProtocols } = require('./external-protocols')
* @return {Promise}
*/
const initialize = async (suite, deploymentId) => {
- const chaindId = hre.network.config.chainId
+ const chainId = hre.network.config.chainId
const [owner] = await ethers.getSigners()
const cache = suite ? null : await fileCache.from(deploymentId)
const network = await getNetworkInfo()
@@ -103,41 +103,41 @@ const initialize = async (suite, deploymentId) => {
await intermediate(cache, protocol, 'addContract', key.PROTOCOL.CNS.STAKING_POOL, stakingPoolContract.address)
// @todo: only applicable to testnet
- await intermediate(cache, crpool, 'approve', stakingPoolContract.address, helper.ether(13_400_300))
+ await intermediate(cache, crpool, 'approve', stakingPoolContract.address, helper.ether(22_094_995_300))
addresses = [npm.address, npmUsdPair.address, crpool.address, crpoolUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(10_000), helper.percentage(0.25), 10949953000, minutesToBlocks(chaindId, 5), helper.ether(13_400_300)]
+ values = [helper.ether(100_000_000), helper.ether(10_000), helper.percentage(0.25), 22_094_995_300, minutesToBlocks(chainId, 45), helper.ether(13_400_300)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('Crpool'), 'Crystalpool Staking', 0, addresses, values)
- await intermediate(cache, hwt, 'approve', stakingPoolContract.address, helper.ether(33_303_000))
+ await intermediate(cache, hwt, 'approve', stakingPoolContract.address, helper.ether(13_522_000_000))
addresses = [npm.address, npmUsdPair.address, hwt.address, hwtUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(10_000), helper.percentage(0.25), 10123480000, minutesToBlocks(chaindId, 5), helper.ether(33_303_000)]
+ values = [helper.ether(100_000_000), helper.ether(10_000), helper.percentage(0.25), 13_522_000_000, minutesToBlocks(chainId, 120), helper.ether(25_303_000)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('Huobi'), 'Huobi Staking', 0, addresses, values)
- await intermediate(cache, obk, 'approve', stakingPoolContract.address, helper.ether(12_30_330))
+ await intermediate(cache, obk, 'approve', stakingPoolContract.address, helper.ether(14_505_290_000))
addresses = [npm.address, npmUsdPair.address, obk.address, obkUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(50_000), helper.percentage(0.25), 35450529000, minutesToBlocks(chaindId, 5), helper.ether(12_30_330)]
+ values = [helper.ether(100_000_000), helper.ether(50_000), helper.percentage(0.25), 14_505_290_000, minutesToBlocks(chainId, 60), helper.ether(16_30_330)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('OBK'), 'OBK Staking', 0, addresses, values)
- await intermediate(cache, sabre, 'approve', stakingPoolContract.address, helper.ether(62_000_000))
+ await intermediate(cache, sabre, 'approve', stakingPoolContract.address, helper.ether(30_330_000_010))
addresses = [npm.address, npmUsdPair.address, sabre.address, sabreUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(100_000), helper.percentage(0.25), 9033000001, minutesToBlocks(chaindId, 5), helper.ether(62_000_000)]
+ values = [helper.ether(100_000_000), helper.ether(100_000), helper.percentage(0.25), 30_330_000_010, minutesToBlocks(chainId, 180), helper.ether(42_000_000)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('SABRE'), 'SABRE Staking', 0, addresses, values)
- await intermediate(cache, bec, 'approve', stakingPoolContract.address, helper.ether(52_000_000))
+ await intermediate(cache, bec, 'approve', stakingPoolContract.address, helper.ether(8_940_330_000))
addresses = [npm.address, npmUsdPair.address, bec.address, becUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(80_000), helper.percentage(0.25), 19403300000, minutesToBlocks(chaindId, 5), helper.ether(52_000_000)]
+ values = [helper.ether(100_000_000), helper.ether(80_000), helper.percentage(0.25), 8_940_330_000, minutesToBlocks(chainId, 60 * 48), helper.ether(27_000_000)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('BEC'), 'BEC Staking', 0, addresses, values)
- await intermediate(cache, xd, 'approve', stakingPoolContract.address, helper.ether(49_000_000))
+ await intermediate(cache, xd, 'approve', stakingPoolContract.address, helper.ether(18_559_222_222))
addresses = [npm.address, npmUsdPair.address, xd.address, xdUsdPair.address]
- values = [helper.ether(100_000_000), helper.ether(190_000), helper.percentage(0.25), 21559222222, minutesToBlocks(chaindId, 5), helper.ether(49_000_000)]
+ values = [helper.ether(100_000_000), helper.ether(190_000), helper.percentage(0.25), 18_559_222_222, minutesToBlocks(chainId, 90), helper.ether(19_000_000)]
await intermediate(cache, stakingPoolContract, 'addOrEditPool', key.toBytes32('XT'), 'XT Staking', 0, addresses, values)
const stakingContract = await deployer.deployWithLibraries(cache, 'CoverStake', {
AccessControlLibV1: libs.accessControlLibV1.address,
BaseLibV1: libs.baseLibV1.address,
CoverUtilV1: libs.coverUtilV1.address,
- RoutineInvokerLibV1: libs.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
NTransferUtilV2: libs.transferLib.address,
ProtoUtilV1: libs.protoUtilV1.address,
StoreKeyUtil: libs.storeKeyUtil.address,
@@ -150,7 +150,7 @@ const initialize = async (suite, deploymentId) => {
AccessControlLibV1: libs.accessControlLibV1.address,
BaseLibV1: libs.baseLibV1.address,
CoverUtilV1: libs.coverUtilV1.address,
- RoutineInvokerLibV1: libs.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
NTransferUtilV2: libs.transferLib.address,
ProtoUtilV1: libs.protoUtilV1.address,
StoreKeyUtil: libs.storeKeyUtil.address,
@@ -172,6 +172,22 @@ const initialize = async (suite, deploymentId) => {
await intermediate(cache, protocol, 'addContract', key.PROTOCOL.CNS.COVER_VAULT_FACTORY, vaultFactory.address)
+ const vaultDelegate = await deployer.deployWithLibraries(cache, 'VaultDelegate',
+ {
+ AccessControlLibV1: libs.accessControlLibV1.address,
+ BaseLibV1: libs.baseLibV1.address,
+ ProtoUtilV1: libs.protoUtilV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
+ StoreKeyUtil: libs.storeKeyUtil.address,
+ StrategyLibV1: libs.strategyLibV1.address,
+ ValidationLibV1: libs.validationLib.address,
+ VaultLibV1: libs.vaultLib.address
+ }
+ , store.address
+ )
+
+ await intermediate(cache, protocol, 'addContract', key.PROTOCOL.CNS.COVER_VAULT_DELEGATE, vaultDelegate.address)
+
const cxTokenFactory = await deployer.deployWithLibraries(cache, 'cxTokenFactory',
{
AccessControlLibV1: libs.accessControlLibV1.address,
@@ -207,7 +223,7 @@ const initialize = async (suite, deploymentId) => {
{
AccessControlLibV1: libs.accessControlLibV1.address,
BaseLibV1: libs.baseLibV1.address,
- RoutineInvokerLibV1: libs.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
StoreKeyUtil: libs.storeKeyUtil.address,
ProtoUtilV1: libs.protoUtilV1.address,
CoverUtilV1: libs.coverUtilV1.address,
@@ -248,7 +264,7 @@ const initialize = async (suite, deploymentId) => {
AccessControlLibV1: libs.accessControlLibV1.address,
BaseLibV1: libs.baseLibV1.address,
PolicyHelperV1: libs.policyHelperV1.address,
- RoutineInvokerLibV1: libs.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
StoreKeyUtil: libs.storeKeyUtil.address,
ValidationLibV1: libs.validationLib.address
}, store.address)
@@ -273,7 +289,7 @@ const initialize = async (suite, deploymentId) => {
AccessControlLibV1: libs.accessControlLibV1.address,
BaseLibV1: libs.baseLibV1.address,
GovernanceUtilV1: libs.governanceLib.address,
- RoutineInvokerLibV1: libs.RoutineInvokerLibV1.address,
+ RoutineInvokerLibV1: libs.routineInvokerLibV1.address,
NTransferUtilV2: libs.transferLib.address,
ProtoUtilV1: libs.protoUtilV1.address,
RegistryLibV1: libs.registryLibV1.address,
diff --git a/util/composer/libs.js b/util/composer/libs.js
index a99a2c7b..ba45ebff 100644
--- a/util/composer/libs.js
+++ b/util/composer/libs.js
@@ -31,7 +31,6 @@ const deployAll = async (cache) => {
})
const strategyLibV1 = await deployer.deployWithLibraries(cache, 'StrategyLibV1', {
- NTransferUtilV2: transferLib.address,
ProtoUtilV1: protoUtilV1.address,
StoreKeyUtil: storeKeyUtil.address
})
@@ -87,9 +86,9 @@ const deployAll = async (cache) => {
const vaultLib = await deployer.deployWithLibraries(cache, 'VaultLibV1', {
CoverUtilV1: coverUtilV1.address,
RoutineInvokerLibV1: routineInvokerLibV1.address,
- NTransferUtilV2: transferLib.address,
ProtoUtilV1: protoUtilV1.address,
PolicyHelperV1: policyHelperV1.address,
+ RegistryLibV1: registryLibV1.address,
StoreKeyUtil: storeKeyUtil.address,
StrategyLibV1: strategyLibV1.address,
ValidationLibV1: validationLib.address
@@ -101,10 +100,9 @@ const deployAll = async (cache) => {
AccessControlLibV1: accessControlLibV1.address,
BaseLibV1: baseLibV1.address,
NTransferUtilV2: transferLib.address,
- RoutineInvokerLibV1: routineInvokerLibV1.address,
- StrategyLibV1: strategyLibV1.address,
- ValidationLibV1: validationLib.address,
- VaultLibV1: vaultLib.address
+ ProtoUtilV1: protoUtilV1.address,
+ RegistryLibV1: registryLibV1.address,
+ ValidationLibV1: validationLib.address
})
const cxTokenFactoryLib = await deployer.deployWithLibraries(cache, 'cxTokenFactoryLibV1', {
@@ -153,7 +151,7 @@ const deployAll = async (cache) => {
stakingPoolCoreLibV1,
stakingPoolLibV1,
priceLibV1,
- RoutineInvokerLibV1: routineInvokerLibV1,
+ routineInvokerLibV1,
strategyLibV1,
policyHelperV1
}
diff --git a/util/composer/token.js b/util/composer/token.js
index 10a700a0..c9f48485 100644
--- a/util/composer/token.js
+++ b/util/composer/token.js
@@ -49,7 +49,7 @@ const deployOrGetFromConfig = async (cache, tokens) => {
}
if (supportedNetworks.indexOf(hre.network.config.chainId) === -1) {
- // throw new Error(`Can't deploy ${symbol} on this network.`)
+ throw new Error(`Can't deploy ${symbol} on this network.`)
}
const contract = await deployer.deploy(cache, 'FakeToken', `Fake ${name}`, symbol, supply || helper.ether(800_000_000))
diff --git a/util/composer/vault.js b/util/composer/vault.js
index 9295e998..41f8b94f 100644
--- a/util/composer/vault.js
+++ b/util/composer/vault.js
@@ -14,10 +14,9 @@ const getVault = async (contracts, coverKey) => {
AccessControlLibV1: contracts.libs.accessControlLibV1.address,
BaseLibV1: contracts.libs.baseLibV1.address,
NTransferUtilV2: contracts.libs.transferLib.address,
- StrategyLibV1: contracts.libs.strategyLibV1.address,
- RoutineInvokerLibV1: contracts.libs.RoutineInvokerLibV1.address,
- ValidationLibV1: contracts.libs.validationLib.address,
- VaultLibV1: contracts.libs.vaultLib.address
+ ProtoUtilV1: contracts.libs.protoUtilV1.address,
+ RegistryLibV1: contracts.libs.registryLibV1.address,
+ ValidationLibV1: contracts.libs.validationLib.address
}
})
diff --git a/util/key.js b/util/key.js
index f37c4282..3f4cea5e 100644
--- a/util/key.js
+++ b/util/key.js
@@ -49,6 +49,7 @@ const PROTOCOL = {
COVER_POLICY_ADMIN: toBytes32('cns:cover:policy:admin'),
COVER_STAKE: toBytes32('cns:cover:stake'),
COVER_VAULT: toBytes32('cns:cover:vault'),
+ COVER_VAULT_DELEGATE: toBytes32('cns:cover:vault:delegate'),
COVER_STABLECOIN: toBytes32('cns:cover:sc'),
COVER_CXTOKEN_FACTORY: toBytes32('cns:cover:cxtoken:factory'),
COVER_VAULT_FACTORY: toBytes32('cns:cover:vault:factory'),
@@ -154,6 +155,7 @@ const PROTOCOL = {
COVER_STAKE: toBytes32('CoverStake'),
COVER_REASSURANCE: toBytes32('CoverReassurance'),
LIQUIDITY_VAULT: toBytes32('Vault'),
+ VAULT_DELEGATE: toBytes32('VaultDelegate'),
LIQUIDITY_ENGINE: toBytes32('LiquidityEngine'),
STRATEGY_AAVE: toBytes32('AaveStrategy'),
STRATEGY_COMPOUND: toBytes32('CompoundStrategy')
diff --git a/util/typedefs.js b/util/typedefs.js
index 60748e3c..2acf486a 100644
--- a/util/typedefs.js
+++ b/util/typedefs.js
@@ -19,7 +19,7 @@
* @property {Object} stakingPoolCoreLibV1 - Staking pool core library
* @property {Object} stakingPoolLibV1 - Staking pool library
* @property {Object} priceLibV1 - Price library
- * @property {Object} RoutineInvokerLibV1 - Housekeeping library
+ * @property {Object} routineInvokerLibV1 - Housekeeping library
* @property {Object} strategyLibV1 - Price library
* @property {Object} policyHelperV1 - Price library
*/