Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions contracts/interfaces/IStakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ interface IStakingPool {
bool isPrivatePool,
uint initialPoolFee,
uint maxPoolFee,
uint _poolId,
string memory ipfsDescriptionHash
uint _poolId
) external;

function processExpirations(bool updateUntilCurrentTimestamp) external;
Expand Down Expand Up @@ -164,8 +163,6 @@ interface IStakingPool {

event PoolFeeChanged(address indexed manager, uint newFee);

event PoolDescriptionSet(string ipfsDescriptionHash);

event Withdraw(address indexed user, uint indexed tokenId, uint tranche, uint amountStakeWithdrawn, uint amountRewardsWithdrawn);

event StakeBurned(uint amount);
Expand Down
8 changes: 8 additions & 0 deletions contracts/interfaces/IStakingProducts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ interface IStakingProducts {
uint bumpedPriceUpdateTime
);

function getPoolManager(uint poolId) external view returns (address);

/* ============= PRICING FUNCTIONS ============= */

function getPremium(
Expand Down Expand Up @@ -117,6 +119,12 @@ interface IStakingProducts {

function changeStakingPoolFactoryOperator(address newOperator) external;

function setPoolMetadata(uint poolId, string memory ipfsHash) external;

function getPoolMetadata(uint poolId) external returns (string memory ipfsHash);

function setInitialMetadata(string[] calldata ipfsHashes) external;

/* ============= EVENTS ============= */

event ProductUpdated(uint productId, uint8 targetWeight, uint96 targetPrice);
Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/generic/StakingPoolGeneric.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "../../interfaces/IStakingPool.sol";

contract StakingPoolGeneric is IStakingPool {

function initialize(bool, uint, uint, uint, string memory) external virtual {
function initialize(bool, uint, uint, uint) external virtual {
revert("Unsupported");
}

Expand Down
17 changes: 16 additions & 1 deletion contracts/mocks/generic/StakingProductsGeneric.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ contract StakingProductsGeneric is IStakingProducts {
revert("Unsupported");
}

function getPoolManager(uint) public pure override returns (address) {
revert("Unsupported");
}

function getPremium(uint, uint, uint, uint, uint, uint, uint, bool, uint, uint) public virtual returns (uint) {
revert("Unsupported");
}
Expand All @@ -36,7 +40,6 @@ contract StakingProductsGeneric is IStakingProducts {
revert("Unsupported");
}


function calculatePremium(StakedProduct memory, uint, uint, uint, uint, uint, uint, uint, uint, uint) public virtual pure returns (uint, StakedProduct memory) {
revert("Unsupported");
}
Expand Down Expand Up @@ -65,4 +68,16 @@ contract StakingProductsGeneric is IStakingProducts {
function changeStakingPoolFactoryOperator(address) external virtual pure {
revert("Unsupported");
}

function setPoolMetadata(uint, string memory) external pure {
revert("Unsupported");
}

function getPoolMetadata(uint) external pure returns (string memory) {
revert("Unsupported");
}

function setInitialMetadata(string[] calldata) external pure {
revert("Unsupported");
}
}
4 changes: 1 addition & 3 deletions contracts/mocks/modules/Cover/COMockStakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,12 @@ contract COMockStakingPool is StakingPoolGeneric {
bool _isPrivatePool,
uint _initialPoolFee,
uint _maxPoolFee,
uint _poolId,
string calldata _ipfsDescriptionHash /* ipfsDescriptionHash */
uint _poolId
) external override {
isPrivatePool = _isPrivatePool;
poolFee = uint8(_initialPoolFee);
maxPoolFee = uint8(_maxPoolFee);
poolId = uint40(_poolId);
ipfsHash = _ipfsDescriptionHash;
}

function requestAllocation(
Expand Down
5 changes: 2 additions & 3 deletions contracts/mocks/modules/Cover/COMockStakingProducts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ contract COMockStakingProducts is StakingProductsGeneric {
uint initialPoolFee,
uint maxPoolFee,
ProductInitializationParams[] memory productInitParams,
string calldata ipfsDescriptionHash
string calldata /*ipfsDescriptionHash*/
) external override returns (uint /*poolId*/, address /*stakingPoolAddress*/) {

uint numProducts = productInitParams.length;
Expand Down Expand Up @@ -110,8 +110,7 @@ contract COMockStakingProducts is StakingProductsGeneric {
isPrivatePool,
initialPoolFee,
maxPoolFee,
poolId,
ipfsDescriptionHash
poolId
);

tokenController().assignStakingPoolManager(poolId, msg.sender);
Expand Down
9 changes: 1 addition & 8 deletions contracts/modules/staking/StakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,7 @@ contract StakingPool is IStakingPool, Multicall {
bool _isPrivatePool,
uint _initialPoolFee,
uint _maxPoolFee,
uint _poolId,
string calldata ipfsDescriptionHash
uint _poolId
) external {

if (msg.sender != address(stakingProducts)) {
Expand All @@ -193,8 +192,6 @@ contract StakingPool is IStakingPool, Multicall {
poolFee = uint8(_initialPoolFee);
maxPoolFee = uint8(_maxPoolFee);
poolId = _poolId.toUint40();

emit PoolDescriptionSet(ipfsDescriptionHash);
}

// updateUntilCurrentTimestamp forces rewards update until current timestamp not just until
Expand Down Expand Up @@ -1315,10 +1312,6 @@ contract StakingPool is IStakingPool, Multicall {
emit PoolPrivacyChanged(msg.sender, _isPrivatePool);
}

function setPoolDescription(string memory ipfsDescriptionHash) external onlyManager {
emit PoolDescriptionSet(ipfsDescriptionHash);
}

/* fixes */

function updateRewardsShares(
Expand Down
85 changes: 62 additions & 23 deletions contracts/modules/staking/StakingProducts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,29 @@ import "../../libraries/StakingPoolLibrary.sol";
contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {
using SafeUintCast for uint;

// pool id => product id => Product
mapping(uint => mapping(uint => StakedProduct)) private _products;
// pool id => { totalEffectiveWeight, totalTargetWeight }
mapping(uint => Weights) public weights;

// pool id => metadata
mapping(uint => string) internal poolMetadata;

address public immutable coverContract;
address public immutable stakingPoolFactory;

uint public constant SURGE_PRICE_RATIO = 2 ether;
uint public constant SURGE_THRESHOLD_RATIO = 90_00; // 90.00%
uint public constant SURGE_THRESHOLD_DENOMINATOR = 100_00; // 100.00%

// base price bump
// +0.2% for each 1% of capacity used, ie +20% for 100%
uint public constant PRICE_BUMP_RATIO = 20_00; // 20%

// bumped price smoothing
// 0.5% per day
uint public constant PRICE_CHANGE_PER_DAY = 200; // 2%

uint public constant INITIAL_PRICE_DENOMINATOR = 100_00;
uint public constant TARGET_PRICE_DENOMINATOR = 100_00;
uint public constant MAX_TOTAL_WEIGHT = 20_00; // 20x
Expand All @@ -41,13 +55,12 @@ contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {
uint public constant ALLOCATION_UNITS_PER_NXM = 100;
uint public constant NXM_PER_ALLOCATION_UNIT = ONE_NXM / ALLOCATION_UNITS_PER_NXM;

// pool id => product id => Product
mapping(uint => mapping(uint => StakedProduct)) private _products;
// pool id => { totalEffectiveWeight, totalTargetWeight }
mapping(uint => Weights) public weights;

address public immutable coverContract;
address public immutable stakingPoolFactory;
modifier onlyManager(uint poolId) {
if (msg.sender != getPoolManager(poolId)) {
revert OnlyManager();
}
_;
}

constructor(address _coverContract, address _stakingPoolFactory) {
coverContract = _coverContract;
Expand Down Expand Up @@ -83,6 +96,14 @@ contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {
);
}

function getPoolManager(uint poolId) public view override returns (address) {
return tokenController().getStakingPoolManager(poolId);
}

function getPoolMetadata(uint poolId) external override view returns (string memory ipfsHash) {
return poolMetadata[poolId];
}

function recalculateEffectiveWeights(uint poolId, uint[] calldata productIds) external {

IStakingPool _stakingPool = stakingPool(poolId);
Expand Down Expand Up @@ -165,7 +186,7 @@ contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {

IStakingPool _stakingPool = stakingPool(poolId);

if (msg.sender != _stakingPool.manager()) {
if (msg.sender != tokenController().getStakingPoolManager(poolId)) {
revert OnlyManager();
}

Expand Down Expand Up @@ -567,29 +588,27 @@ contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {
uint initialPoolFee,
uint maxPoolFee,
ProductInitializationParams[] memory productInitParams,
string calldata ipfsDescriptionHash
) external whenNotPaused onlyMember returns (uint /*poolId*/, address /*stakingPoolAddress*/) {
string calldata ipfsHash
) external override whenNotPaused onlyMember returns (uint /*poolId*/, address /*stakingPoolAddress*/) {

ICoverProducts _coverProducts = coverProducts();

ProductInitializationParams[] memory initializedProducts = _coverProducts.prepareStakingProductsParams(
productInitParams
);

// create and initialize staking pool
(uint poolId, address stakingPoolAddress) = ICompleteStakingPoolFactory(stakingPoolFactory).create(coverContract);
IStakingPool(stakingPoolAddress).initialize(isPrivatePool, initialPoolFee, maxPoolFee, poolId);

IStakingPool(stakingPoolAddress).initialize(
isPrivatePool,
initialPoolFee,
maxPoolFee,
poolId,
ipfsDescriptionHash
);

// assign pool manager
tokenController().assignStakingPoolManager(poolId, msg.sender);

// set products
ProductInitializationParams[] memory initializedProducts = _coverProducts.prepareStakingProductsParams(
productInitParams
);
_setInitialProducts(poolId, initializedProducts);

// set metadata
poolMetadata[poolId] = ipfsHash;

return (poolId, stakingPoolAddress);
}

Expand Down Expand Up @@ -637,11 +656,31 @@ contract StakingProducts is IStakingProducts, MasterAwareV2, Multicall {
});
}

// future role transfers
// future operator role transfers
function changeStakingPoolFactoryOperator(address _operator) external onlyInternal {
ICompleteStakingPoolFactory(stakingPoolFactory).changeOperator(_operator);
}

function setPoolMetadata(
uint poolId,
string memory ipfsHash
) external override onlyManager(poolId) {
poolMetadata[poolId] = ipfsHash;
}

// temporary migration function

function setInitialMetadata(string[] calldata ipfsHashes) external onlyAdvisoryBoard {

uint poolCount = IStakingPoolFactory(stakingPoolFactory).stakingPoolCount();
require(ipfsHashes.length == poolCount, "StakingProducts: Metadata length mismatch");
require(bytes(poolMetadata[1]).length == 0, "StakingProducts: Metadata already set");

for (uint i = 0; i < poolCount; i++) {
poolMetadata[i + 1] = ipfsHashes[i];
}
}

/* dependencies */

function tokenController() internal view returns (ITokenController) {
Expand Down
4 changes: 1 addition & 3 deletions test/unit/StakingPool/burnStake.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const poolInitParams = {
initialPoolFee: 5, // 5%
maxPoolFee: 5, // 5%
products: [initialProduct],
ipfsDescriptionHash: 'Description Hash',
};

const productTypeFixture = {
Expand Down Expand Up @@ -77,7 +76,7 @@ async function burnStakeSetup() {
const fixture = await loadFixture(setup);
const { stakingPool, stakingProducts, coverProducts } = fixture;
const [staker] = fixture.accounts.members;
const { poolId, initialPoolFee, maxPoolFee, products, ipfsDescriptionHash } = poolInitParams;
const { poolId, initialPoolFee, maxPoolFee, products } = poolInitParams;

await coverProducts.setProductType(productTypeFixture, initialProduct.productId);
await coverProducts.setProduct(coverProductTemplate, initialProduct.productId);
Expand All @@ -87,7 +86,6 @@ async function burnStakeSetup() {
initialPoolFee,
maxPoolFee,
poolId,
ipfsDescriptionHash,
);

await stakingProducts.connect(fixture.stakingProductsSigner).setInitialProducts(poolId, products);
Expand Down
4 changes: 1 addition & 3 deletions test/unit/StakingPool/depositTo.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const poolInitParams = {
initialPoolFee: 5, // 5%
maxPoolFee: 5, // 5%
products: [productParams],
ipfsDescriptionHash: 'Description Hash',
};

const managerDepositId = 0;
Expand All @@ -43,14 +42,13 @@ async function depositToSetup() {
const fixture = await loadFixture(setup);
const { stakingPool, stakingProducts, tokenController } = fixture;
const { defaultSender: manager } = fixture.accounts;
const { poolId, initialPoolFee, maxPoolFee, products, ipfsDescriptionHash } = poolInitParams;
const { poolId, initialPoolFee, maxPoolFee, products } = poolInitParams;

await stakingPool.connect(fixture.stakingProductsSigner).initialize(
false, // isPrivatePool
initialPoolFee,
maxPoolFee,
poolId,
ipfsDescriptionHash,
);
await tokenController.setStakingPoolManager(poolId, manager.address);

Expand Down
7 changes: 2 additions & 5 deletions test/unit/StakingPool/extendDeposit.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const poolInitParams = {
initialPoolFee: 5, // 5%
maxPoolFee: 5, // 5%
products: [productParams],
ipfsDescriptionHash: 'Description Hash',
};

const depositNftId = 1;
Expand All @@ -39,10 +38,8 @@ async function extendDepositSetup() {
const [user] = fixture.accounts.members;
const manager = fixture.accounts.defaultSender;

const { poolId, initialPoolFee, maxPoolFee, products, ipfsDescriptionHash } = poolInitParams;
await stakingPool
.connect(fixture.stakingProductsSigner)
.initialize(false, initialPoolFee, maxPoolFee, poolId, ipfsDescriptionHash);
const { poolId, initialPoolFee, maxPoolFee, products } = poolInitParams;
await stakingPool.connect(fixture.stakingProductsSigner).initialize(false, initialPoolFee, maxPoolFee, poolId);
await tokenController.setStakingPoolManager(poolId, manager.address);

await stakingProducts.connect(fixture.stakingProductsSigner).setInitialProducts(poolId, products);
Expand Down
Loading