Skip to content
Merged
162 changes: 137 additions & 25 deletions contracts/mocks/Cover/CoverMockStakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,36 @@ pragma solidity ^0.8.9;
import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-v4/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts-v4/utils/Strings.sol";
import "@openzeppelin/contracts-v4/token/ERC721/ERC721.sol";

import "../../modules/staking/StakingPool.sol";

contract CoverMockStakingPool is StakingPool {

contract CoverMockStakingPool is IStakingPool, ERC721 {

/* immutables */
address public immutable memberRoles;

mapping (uint => uint) public usedCapacity;
mapping (uint => uint) public stakedAmount;

// product id => Product
mapping(uint => Product) public products;
mapping (uint => uint) public mockPrices;

uint public constant MAX_PRICE_RATIO = 1e20;
uint public constant MAX_PRICE_RATIO = 10_000;
uint constant REWARDS_DENOMINATOR = 10_000;

uint public poolId;
// erc721 supply
uint public totalSupply;
address public manager;

constructor (
address _nxm,
address _coverContract,
ITokenController _tokenController,
address _memberRoles
)
StakingPool("Nexus Mutual Staking Pool", "NMSPT", _nxm, _coverContract, _tokenController)
) ERC721("Nexus Mutual Staking Pool", "NMSPT")
{
memberRoles = _memberRoles;
}
Expand All @@ -35,28 +43,60 @@ contract CoverMockStakingPool is StakingPool {
return string(abi.encodePacked(super.name(), " ", Strings.toString(poolId)));
}

function initialize(address _manager, uint _poolId) external /*override*/ {
function initialize(
address _manager,
bool _isPrivatePool,
uint _initialPoolFee,
uint _maxPoolFee,
ProductInitializationParams[] calldata params,
uint _poolId
) external {
_isPrivatePool;
_initialPoolFee;
_maxPoolFee;
params;
manager = _manager;
_mint(_manager, totalSupply++);
poolId = _poolId;
}


function operatorTransferFrom(address from, address to, uint256 amount) external /*override*/ {
require(msg.sender == memberRoles, "StakingPool: Caller is not MemberRoles");
_transfer(from, to, amount);
}

function allocateCapacity(

function allocateStake(
CoverRequest calldata request
) external override returns (uint allocatedAmount, uint premium, uint rewardsInNXM) {

usedCapacity[request.productId] += request.amount;

uint premium = calculatePremium(mockPrices[request.productId], request.amount, request.period);

return (
request.amount,
premium,
premium * request.rewardRatio / REWARDS_DENOMINATOR
);
}

function deallocateStake(
uint productId,
uint amountInNXM,
uint start,
uint period,
uint rewardRatio,
uint initialPriceRatio
) external /*override*/ returns (uint coveredAmountInNXM, uint premiumInNXM) {
uint amount,
uint premium,
uint globalRewardsRatio
) external {

// silence compiler warnings
productId;
start;
period;
rewardRatio;
initialPriceRatio;
usedCapacity[productId] += amountInNXM;
return (amountInNXM, calculatePremium(mockPrices[productId], amountInNXM, period));
amount;
premium;
}

function calculatePremium(uint priceRatio, uint coverAmount, uint period) public pure returns (uint) {
Expand All @@ -67,15 +107,20 @@ contract CoverMockStakingPool is StakingPool {
_mint(msg.sender, amount);
}

function freeCapacity(
uint productId,
uint previousPeriod,
uint previousStartTime,
uint previousRewardAmount,
uint periodReduction,
uint coveredAmount
) external /*override*/ {
// no-op
// used to transfer all nfts when a user switches the membership to a new address
function operatorTransfer(
address from,
address to,
uint[] calldata tokenIds
) external {
uint length = tokenIds.length;
for (uint i = 0; i < length; i++) {
_safeTransfer(from, to, tokenIds[i], "");
}
}

function updateTranches() external {
revert("CoverMockStakingPool: not callable");
}

function getAvailableCapacity(uint productId, uint capacityFactor) external /*override*/ view returns (uint) {
Expand All @@ -102,7 +147,7 @@ contract CoverMockStakingPool is StakingPool {
usedCapacity[productId] = amount;
}

function setTargetPrice(uint productId, uint amount) external {
function setTargetPrice(uint productId, uint amount) external {
products[productId].targetPrice = uint96(amount);
}

Expand All @@ -121,4 +166,71 @@ contract CoverMockStakingPool is StakingPool {
function changeDependentContractAddress() external {
// noop
}

function burnStake(uint productId, uint start, uint period, uint amount) external {
productId;
start;
period;
amount;

// no-op
}

function depositTo(DepositRequest[] memory requests) external returns (uint[] memory tokenIds) {
revert("CoverMockStakingPool: not callable");
}

function withdraw(WithdrawRequest[] memory params) external {
revert("CoverMockStakingPool: not callable");

}

function addProducts(ProductParams[] memory params) external {
revert("CoverMockStakingPool: not callable");
}

function removeProducts(uint[] memory productIds) external {
revert("CoverMockStakingPool: not callable");
}

function setProductDetails(ProductParams[] memory params) external {
revert("CoverMockStakingPool: not callable");
}

function setPoolFee(uint newFee) external {
revert("CoverMockStakingPool: not callable");
}

function setPoolPrivacy(bool isPrivatePool) external {
revert("CoverMockStakingPool: not callable");
}


function getActiveStake() external view returns (uint) {
revert("CoverMockStakingPool: not callable");
}

function getProductStake(uint productId, uint coverExpirationDate) external view returns (uint) {
revert("CoverMockStakingPool: not callable");
}

function getFreeProductStake(uint productId, uint coverExpirationDate) external view returns (uint) {
revert("CoverMockStakingPool: not callable");
}

function getAllocatedProductStake(uint productId) external view returns (uint) {
revert("CoverMockStakingPool: not callable");
}

function getPriceParameters(
uint productId,
uint maxCoverPeriod
) external override view returns (
uint activeCover,
uint[] memory staked,
uint lastBasePrice,
uint targetPrice
) {
revert("CoverMockStakingPool: not callable");
}
}
21 changes: 20 additions & 1 deletion contracts/mocks/TokenControllerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import "../modules/token/NXMToken.sol";

contract TokenControllerMock is MasterAware {

struct StakingPoolNXMBalances {
uint128 rewards;
uint128 deposits;
}

NXMToken public token;
address public addToWhitelistLastCalledWtih;
address public removeFromWhitelistLastCalledWtih;

mapping(uint => StakingPoolNXMBalances) stakingPoolNXMBalances;

function mint(address _member, uint256 _amount) public onlyInternal {
token.mint(_member, _amount);
}
Expand Down Expand Up @@ -39,7 +46,19 @@ contract TokenControllerMock is MasterAware {
return true;
}

/* unused functions */
function mintStakingPoolNXMRewards(uint amount, uint poolId) external {

mint(address(this), amount);
stakingPoolNXMBalances[poolId].rewards += uint128(amount);
}

function burnStakingPoolNXMRewards(uint amount, uint poolId) external {

burnFrom(address(this), amount);
stakingPoolNXMBalances[poolId].rewards -= uint128(amount);
}

/* unused functions */

modifier unused {
require(false, "Unexpected TokenControllerMock call");
Expand Down
30 changes: 14 additions & 16 deletions contracts/modules/cover/Cover.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ contract Cover is ICover, MasterAwareV2, IStakingPoolBeacon {

uint public constant MAX_COVER_PERIOD = 364 days;
uint private constant MIN_COVER_PERIOD = 28 days;
// this constant is used for calculating the normalized yearly percentage cost of cover
uint private constant ONE_YEAR = 365 days;

uint private constant MAX_COMMISSION_RATIO = 2500; // 25%

Expand Down Expand Up @@ -253,7 +255,7 @@ contract Cover is ICover, MasterAwareV2, IStakingPoolBeacon {
// priceRatio is normalized on a per year basis (eg. 1.5% per year)
uint16 priceRatio = SafeUintCast.toUint16(
divRound(
totalPremiumInNXM * PRICE_DENOMINATOR * MAX_COVER_PERIOD / params.period,
totalPremiumInNXM * PRICE_DENOMINATOR * ONE_YEAR / params.period,
totalCoverAmountInNXM
)
);
Expand All @@ -280,10 +282,6 @@ contract Cover is ICover, MasterAwareV2, IStakingPoolBeacon {
Product memory product = _products[params.productId];
uint gracePeriod = _productTypes[product.productType].gracePeriodInDays * 1 days;

if (true) {
// wrapped in if(true) to avoid the compiler warning about unreachable code
revert("capacity calculation: not implemented");
}

return _stakingPool.allocateStake(
CoverRequest(
Expand Down Expand Up @@ -418,7 +416,6 @@ contract Cover is ICover, MasterAwareV2, IStakingPoolBeacon {
);
}

// TODO: implement properly. we need the staking interface for burning.
function performPayoutBurn(
uint coverId,
uint segmentId,
Expand Down Expand Up @@ -533,24 +530,25 @@ contract Cover is ICover, MasterAwareV2, IStakingPoolBeacon {
bool isPrivatePool,
uint initialPoolFee,
uint maxPoolFee,
ProductInitializationParams[] memory params,
ProductInitializationParams[] memory productInitializationParams,
uint depositAmount,
uint trancheId
) external returns (address stakingPoolAddress) {

emit StakingPoolCreated(stakingPoolAddress, manager, stakingPoolImplementation);

// [todo] handle the creation of NFT 0 which is the default NFT owned by the pool manager
CoverUtilsLib.PoolInitializationParams memory poolInitializationParams = CoverUtilsLib.PoolInitializationParams(
stakingPoolCount++,
manager,
isPrivatePool,
initialPoolFee,
maxPoolFee
);

return CoverUtilsLib.createStakingPool(
_products,
CoverUtilsLib.PoolInitializationParams(
stakingPoolCount++,
manager,
isPrivatePool,
initialPoolFee,
maxPoolFee
),
params,
poolInitializationParams,
productInitializationParams,
depositAmount,
trancheId,
master.getLatestAddress("PS")
Expand Down
2 changes: 1 addition & 1 deletion contracts/modules/cover/CoverUtilsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "../../interfaces/ITokenController.sol";
import "../../libraries/SafeUintCast.sol";
import "./MinimalBeaconProxy.sol";


library CoverUtilsLib {

struct MigrateParams {
Expand Down Expand Up @@ -124,7 +125,6 @@ library CoverUtilsLib {
new MinimalBeaconProxy{ salt: bytes32(poolInitParams.poolId) }(address(this))
);


if (msg.sender != pooledStakingAddress) {

// override with initial price
Expand Down
Loading