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
10 changes: 10 additions & 0 deletions contracts/interfaces/IPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ interface IPool {
uint8 decimals;
}

struct NewPoolAssets {
Asset[] assets;
SwapDetails[] swapDetails;
}

function investmentAssets(uint index) external view returns (
address assetAddress,
uint8 decimals
Expand Down Expand Up @@ -82,4 +87,9 @@ interface IPool {
function deprecatedCoverAssetsBitmap() external view returns (uint32);

function setSwapValue(uint value) external;

function getAssetsAndSwapDetails() external view returns (
NewPoolAssets memory _coverAssets,
NewPoolAssets memory _investmentAssets
);
}
111 changes: 86 additions & 25 deletions contracts/modules/capital/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ contract Pool is IPool, MasterAwareV2, ReentrancyGuard {

uint16 constant MAX_SLIPPAGE_DENOMINATOR = 10000;


/* events */
event Payout(address indexed to, address indexed assetAddress, uint amount);
event NXMSold (address indexed member, uint nxmIn, uint ethOut);
Expand All @@ -77,44 +78,45 @@ contract Pool is IPool, MasterAwareV2, ReentrancyGuard {
_;
}


constructor (
address _master,
address _priceOracle,
address _swapOperator,
address DAIAddress,
address stETHAddress
NewPoolAssets memory _newCoverAssets,
NewPoolAssets memory _newInvestmentAssets
) {
master = INXMMaster(_master);
priceFeedOracle = IPriceFeedOracle(_priceOracle);
swapOperator = _swapOperator;
// Push ETH as cover asset (no swap details)
coverAssets.push(Asset(ETH, 18));

// [todo] After this contract is deployed it might be worth modifying upgradeCapitalPool to
// copy the assets on future upgrades instead of having them hardcoded in the constructor.
// issue: https://github.com/NexusMutual/smart-contracts/issues/473
IPool previousPool = IPool(master.getLatestAddress("P1"));

// The order of coverAssets should never change between updates. Do not remove the following
// lines!
coverAssets.push(Asset(ETH, 18));
coverAssets.push(Asset(DAIAddress, 18));
// If this is an upgrade, copy cover and investment assets from previous pool
if (address(previousPool) != address(0)) {
(
NewPoolAssets memory _coverAssets,
NewPoolAssets memory _investmentAssets
) = previousPool.getAssetsAndSwapDetails();

// Add investment assets
investmentAssets.push(Asset(stETHAddress, 18));
// Make sure first asset is ETH
require(_coverAssets.assets[0].assetAddress == ETH, "Pool: First cover asset must be ETH");

// Set DAI swap details
swapDetails[DAIAddress] = SwapDetails(
1000000 ether, // minAmount (1 mil)
2000000 ether, // maxAmount (2 mil)
0, // lastSwapTime
250 // maxSlippageRatio (0.25%)
);
// Add cover assets, skipping the first cover asset (ETH)
_addAssets(_coverAssets, true, 1);
// Add all investment assets
_addAssets(_investmentAssets, false, 0);
}

// Set stETH swap details
swapDetails[stETHAddress] = SwapDetails(
24360 ether, // minAmount (~24k)
32500 ether, // maxAmount (~32k)
1633425218, // lastSwapTime
0 // maxSlippageRatio (0%)
);
if (_newCoverAssets.assets.length > 0) {
_addAssets(_newCoverAssets, true, 0);
}

if (_newInvestmentAssets.assets.length > 0) {
_addAssets(_newInvestmentAssets, false, 0);
}
}

fallback() external payable {}
Expand Down Expand Up @@ -202,6 +204,31 @@ contract Pool is IPool, MasterAwareV2, ReentrancyGuard {
return swapDetails[assetAddress];
}

function getAssetsAndSwapDetails() external view returns (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we exclude abandoned assets from being copied over? I could change this function so it only returns active assets.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean by abandoned what do you check for that?

NewPoolAssets memory _coverAssets,
NewPoolAssets memory _investmentAssets
)
{
_coverAssets.assets = coverAssets;
uint count = _coverAssets.assets.length;
_coverAssets.swapDetails = new SwapDetails[](count);

for (uint i = 0; i < count; i++) {
_coverAssets.swapDetails[i] = swapDetails[_coverAssets.assets[i].assetAddress];
}

_investmentAssets.assets = investmentAssets;
count = _investmentAssets.assets.length;
_investmentAssets.swapDetails = new SwapDetails[](count);

for (uint i = 0; i < count; i++) {
_investmentAssets.swapDetails[i] = swapDetails[_coverAssets.assets[i].assetAddress];
}

return (_coverAssets, _investmentAssets);

}

function addAsset(
address assetAddress,
uint8 decimals,
Expand All @@ -210,6 +237,38 @@ contract Pool is IPool, MasterAwareV2, ReentrancyGuard {
uint16 _maxSlippageRatio,
bool isCoverAsset
) external onlyGovernance {
_addAsset(assetAddress, decimals, _min, _max, _maxSlippageRatio, isCoverAsset);
}

function _addAssets(
NewPoolAssets memory assets,
bool areCoverAssets,
uint startIndex
) internal {
uint assetCount = assets.assets.length;
require(assets.swapDetails.length == assetCount, "Pool: Assets and SwapDetails length mismatch");
for (;startIndex < assetCount; startIndex++) {
Asset memory asset = assets.assets[startIndex];
SwapDetails memory details = assets.swapDetails[startIndex];
_addAsset(
asset.assetAddress,
asset.decimals,
details.minAmount,
details.maxAmount,
details.maxSlippageRatio,
areCoverAssets
);
}
}

function _addAsset(
address assetAddress,
uint8 decimals,
uint104 _min,
uint104 _max,
uint16 _maxSlippageRatio,
bool isCoverAsset
) internal {
require(assetAddress != address(0), "Pool: Asset is zero address");
require(_max >= _min, "Pool: max < min");
require(_maxSlippageRatio <= MAX_SLIPPAGE_DENOMINATOR, "Pool: Max slippage ratio > 1");
Expand Down Expand Up @@ -390,6 +449,8 @@ contract Pool is IPool, MasterAwareV2, ReentrancyGuard {

// Transfer cover assets. Start from 1 (0 is ETH)
uint coverAssetsCount = coverAssets.length;

// Skip ETH
for (uint i = 1; i < coverAssetsCount; i++) {
_transferEntireAssetBalance(coverAssets[i].assetAddress, newPoolAddress);
}
Expand Down
32 changes: 16 additions & 16 deletions lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
const { hex } = require('./helpers');

const StakingUintParamType = {
MIN_STAKE: hex('MIN_STAK'),
MAX_EXPOSURE: hex('MAX_EXPO'),
MIN_UNSTAKE: hex('MIN_UNST'),
UNSTAKE_LOCK_TIME: hex('UNST_LKT'),
MIN_STAKE: hex('MIN_STAK'.padEnd(8, '\0')),
MAX_EXPOSURE: hex('MAX_EXPO'.padEnd(8, '\0')),
MIN_UNSTAKE: hex('MIN_UNST'.padEnd(8, '\0')),
UNSTAKE_LOCK_TIME: hex('UNST_LKT'.padEnd(8, '\0')),
};

const PoolUintParamType = {
minPoolEth: hex('MIN_ETH'),
minPoolEth: hex('MIN_ETH'.padEnd(8, '\0')),
};

const PoolAddressParamType = {
swapOperator: hex('SWP_OP'),
priceFeedOracle: hex('PRC_FEED'),
swapOperator: hex('SWP_OP'.padEnd(8, '\0')),
priceFeedOracle: hex('PRC_FEED'.padEnd(8, '\0')),
};

const MCRUintParamType = {
mcrFloorIncrementThreshold: hex('DMCT'),
maxMCRFloorIncrement: hex('DMCI'),
maxMCRIncrement: hex('MMIC'),
gearingFactor: hex('GEAR'),
minUpdateTime: hex('MUTI'),
mcrFloorIncrementThreshold: hex('DMCT'.padEnd(8, '\0')),
maxMCRFloorIncrement: hex('DMCI'.padEnd(8, '\0')),
maxMCRIncrement: hex('MMIC'.padEnd(8, '\0')),
gearingFactor: hex('GEAR'.padEnd(8, '\0')),
minUpdateTime: hex('MUTI'.padEnd(8, '\0')),
};

const NXMasterOwnerParamType = {
msWallet: hex('MSWALLET'),
quotationAuthority: hex('QUOAUTH'),
kycAuthority: hex('KYCAUTH'),
emergencyAdmin: hex('EMADMIN'),
msWallet: hex('MSWALLET'.padEnd(8, '\0')),
quotationAuthority: hex('QUOAUTH'.padEnd(8, '\0')),
kycAuthority: hex('KYCAUTH'.padEnd(8, '\0')),
emergencyAdmin: hex('EMADMIN'.padEnd(8, '\0')),
};

const Role = {
Expand Down
2 changes: 1 addition & 1 deletion test/integration/Master/master.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe('master', function () {
const IndividualClaims = await ethers.getContractFactory('IndividualClaims');
const LegacyPooledStaking = await ethers.getContractFactory('LegacyPooledStaking');

const pool = await Pool.deploy(master.address, priceFeedOracle.address, AddressZero, dai.address, AddressZero);
const pool = await Pool.deploy(master.address, priceFeedOracle.address, AddressZero, [[], []], [[], []]);

const contractCodes = ['TC', 'CL', 'CR', 'P1', 'MC', 'GV', 'PC', 'MR', 'PS', 'GW', 'IC'];
const newAddresses = [
Expand Down
61 changes: 52 additions & 9 deletions test/integration/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,58 @@ async function setup() {
// trigger initialize and update master address
await disposableMCR.initializeNextMcr(mc.address, master.address);

const p1 = await Pool.deploy(master.address, priceFeedOracle.address, AddressZero, dai.address, stETH.address);
const coverAssets = {
assets: [
{
// dai
decimals: 18,
assetAddress: dai.address,
},
{
// usdc
decimals: usdcDecimals,
assetAddress: usdc.address,
},
],
swapDetails: [
{
// dai
minAmount: parseEther('1000000'),
maxAmount: parseEther('2000000'),
maxSlippageRatio: 250,
lastSwapTime: 0,
},
// usdc
{
minAmount: parseUnits('1000000', usdcDecimals),
maxAmount: parseUnits('2000000', usdcDecimals),
maxSlippageRatio: 250,
lastSwapTime: 0,
},
],
};

const investmentAssets = {
assets: [
{
// stETH
decimals: 18,
assetAddress: stETH.address,
},
],
swapDetails: [
{
// stEth
minAmount: parseEther('24360'),
maxAmount: parseEther('32500'),
maxSlippageRatio: 0,
lastSwapTime: 0,
},
],
};

// Deploy pool and set cover assets above
const p1 = await Pool.deploy(master.address, priceFeedOracle.address, AddressZero, coverAssets, investmentAssets);

const cowVaultRelayer = await SOMockVaultRelayer.deploy();
const cowSettlement = await SOMockSettlement.deploy(cowVaultRelayer.address);
Expand Down Expand Up @@ -403,14 +454,6 @@ async function setup() {
]);

await p1.updateAddressParameters(hex('SWP_OP').padEnd(2 + 16, '0'), swapOperator.address);
await p1.addAsset(
usdc.address,
usdcDecimals,
parseUnits('1000000', usdcDecimals),
parseUnits('2000000', usdcDecimals),
250,
true,
);

await cover.updateUintParametersDisposable(
[0, 1], // CoverUintParams.globalCapacityRatio, CoverUintParams.globalRewardsRatio
Expand Down
Loading