Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/replace idset with oz enumerableset #56

Merged
merged 2 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"solidity.remappingsUnix": [
"@openzeppelin/=/home/vscode/.brownie/packages/OpenZeppelin/openzeppelin-contracts@4.7.0",
"@chainlink/=/home/vscode/.brownie/packages/smartcontractkit/chainlink@1.6.0",
"@etherisc/gif-interface/=/home/vscode/.brownie/packages/etherisc/gif-interface@ac0714e",
"@etherisc/gif-interface/=/home/vscode/.brownie/packages/etherisc/gif-interface@c958220",
],
"peacock.remoteColor": "1D3C43",
"workbench.colorCustomizations": {
Expand Down
5 changes: 3 additions & 2 deletions brownie-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ compiler:
remappings:
- "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.7.0"
- "@chainlink=smartcontractkit/chainlink@1.6.0"
- "@etherisc/gif-interface=etherisc/gif-interface@ac0714e"
- "@etherisc/gif-interface=etherisc/gif-interface@c958220"

# packages below will be added to brownie
# you may use 'brownie pm list' after 'brownie compile'
Expand All @@ -34,7 +34,7 @@ dependencies:
# github dependency format: <owner>/<repository>@<release>
- OpenZeppelin/openzeppelin-contracts@4.7.0
- smartcontractkit/chainlink@1.6.0
- etherisc/gif-interface@ac0714e
- etherisc/gif-interface@c958220

# exclude open zeppeling contracts when calculating test coverage
# https://eth-brownie.readthedocs.io/en/v1.10.3/config.html#exclude_paths
Expand All @@ -49,6 +49,7 @@ reports:
- Context
- Ownable
- EnumerableMap
- EnumerableSet
- ERC1967Proxy
- ERC20
- ERC721
Expand Down
49 changes: 40 additions & 9 deletions contracts/modules/PoolController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import "@etherisc/gif-interface/contracts/components/IComponent.sol";
import "@etherisc/gif-interface/contracts/components/IRiskpool.sol";


import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract PoolController is
IPool,
CoreController
{

using EnumerableSet for EnumerableSet.UintSet;

// used for representation of collateralization
// collateralization between 0 and 1 (1=100%)
// value might be larger when overcollateralization
Expand All @@ -34,7 +38,7 @@ contract PoolController is

mapping(uint256 /* riskpoolId */ => uint256 /* maxmimumNumberOfActiveBundles */) private _maxmimumNumberOfActiveBundlesForRiskpoolId;

mapping(uint256 /* riskpoolId */ => uint256 /* numberOfActiveBundles */) private _numberOfActiveBundlesForRiskpoolId;
mapping(uint256 /* riskpoolId */ => EnumerableSet.UintSet /* active bundle id set */) private _activeBundleIdsForRiskpoolId;

uint256 [] private _riskpoolIds;

Expand Down Expand Up @@ -300,18 +304,45 @@ contract PoolController is
return _riskpoolIdForProductId[productId];
}

function increaseNumberOfActiveBundles(uint256 riskpoolId) external
onlyRiskpoolService
function activeBundles(uint256 riskpoolId) external view returns(uint256 numberOfActiveBundles) {
return EnumerableSet.length(_activeBundleIdsForRiskpoolId[riskpoolId]);
}

function getActiveBundleId(uint256 riskpoolId, uint256 bundleIdx) external view returns(uint256 bundleId) {
require(
bundleIdx < EnumerableSet.length(_activeBundleIdsForRiskpoolId[riskpoolId]),
"ERROR:POL-041:BUNDLE_IDX_TOO_LARGE"
);

return EnumerableSet.at(_activeBundleIdsForRiskpoolId[riskpoolId], bundleIdx);
}

function addBundleIdToActiveSet(uint256 riskpoolId, uint256 bundleId)
external
onlyRiskpoolService
{
require(_numberOfActiveBundlesForRiskpoolId[riskpoolId] < _maxmimumNumberOfActiveBundlesForRiskpoolId[riskpoolId], "ERROR:POL-041:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED");
_numberOfActiveBundlesForRiskpoolId[riskpoolId]++;
require(
!EnumerableSet.contains(_activeBundleIdsForRiskpoolId[riskpoolId], bundleId),
"ERROR:POL-042:BUNDLE_ID_ALREADY_IN_SET"
);
require(
EnumerableSet.length(_activeBundleIdsForRiskpoolId[riskpoolId]) < _maxmimumNumberOfActiveBundlesForRiskpoolId[riskpoolId],
"ERROR:POL-043:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"
);

EnumerableSet.add(_activeBundleIdsForRiskpoolId[riskpoolId], bundleId);
}

function decreaseNumberOfActiveBundles(uint256 riskpoolId) external
onlyRiskpoolService
function removeBundleIdFromActiveSet(uint256 riskpoolId, uint256 bundleId)
external
onlyRiskpoolService
{
require(_numberOfActiveBundlesForRiskpoolId[riskpoolId] > 0, "ERROR:POL-042:NO_ACTIVE_BUNDLES");
_numberOfActiveBundlesForRiskpoolId[riskpoolId]--;
require(
EnumerableSet.contains(_activeBundleIdsForRiskpoolId[riskpoolId], bundleId),
"ERROR:POL-044:BUNDLE_ID_NOT_IN_SET"
);

EnumerableSet.remove(_activeBundleIdsForRiskpoolId[riskpoolId], bundleId);
}

function getFullCollateralizationLevel() external pure returns (uint256) {
Expand Down
8 changes: 7 additions & 1 deletion contracts/services/InstanceService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,17 @@ contract InstanceService is
return _pool.getRiskpool(riskpoolId).balance;
}

function activeBundles(uint256 riskpoolId) external override view returns(uint256 numberOfActiveBundles) {
return _pool.activeBundles(riskpoolId);
}

function getActiveBundleId(uint256 riskpoolId, uint256 bundleIdx) external override view returns(uint256 bundleId) {
return _pool.getActiveBundleId(riskpoolId, bundleIdx);
}
function getMaximumNumberOfActiveBundles(uint256 riskpoolId) external override view returns(uint256 maximumNumberOfActiveBundles) {
return _pool.getMaximumNumberOfActiveBundles(riskpoolId);
}


/* bundle */
function getBundleToken() external override view returns(IBundleToken token) {
BundleToken bundleToken = _bundle.getToken();
Expand Down
16 changes: 8 additions & 8 deletions contracts/services/RiskpoolService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import "@etherisc/gif-interface/contracts/components/IComponent.sol";
import "@etherisc/gif-interface/contracts/modules/IBundle.sol";
import "@etherisc/gif-interface/contracts/services/IRiskpoolService.sol";

// TODO create/lock/unlock notify poolcontroller about the action and poolcontoller updates active bundles

contract RiskpoolService is
IRiskpoolService,
CoreController
Expand Down Expand Up @@ -127,8 +125,9 @@ contract RiskpoolService is
returns(uint256 bundleId)
{
uint256 riskpoolId = _component.getComponentId(_msgSender());
_pool.increaseNumberOfActiveBundles(riskpoolId);
bundleId = _bundle.create(owner, riskpoolId, filter, 0);

_pool.addBundleIdToActiveSet(riskpoolId, bundleId);

(uint256 fee, uint256 netCapital) = _treasury.processCapital(bundleId, initialCapital);

Expand Down Expand Up @@ -182,7 +181,7 @@ contract RiskpoolService is
onlyOwningRiskpool(bundleId, true)
{
uint256 riskpoolId = _component.getComponentId(_msgSender());
_pool.decreaseNumberOfActiveBundles(riskpoolId);
_pool.removeBundleIdFromActiveSet(riskpoolId, bundleId);
_bundle.lock(bundleId);
}

Expand All @@ -192,7 +191,7 @@ contract RiskpoolService is
onlyOwningRiskpool(bundleId, true)
{
uint256 riskpoolId = _component.getComponentId(_msgSender());
_pool.increaseNumberOfActiveBundles(riskpoolId);
_pool.addBundleIdToActiveSet(riskpoolId, bundleId);
_bundle.unlock(bundleId);
}

Expand All @@ -202,10 +201,11 @@ contract RiskpoolService is
onlyOwningRiskpool(bundleId, true)
{
uint256 riskpoolId = _component.getComponentId(_msgSender());
// only decrease active bundles when riskpool is active - locked riskpool is not counted towards active bundles
if (_component.getComponentState(riskpoolId) == IComponent.ComponentState.Active) {
_pool.decreaseNumberOfActiveBundles(riskpoolId);

if (_bundle.getState(bundleId) == IBundle.BundleState.Active) {
_pool.removeBundleIdFromActiveSet(riskpoolId, bundleId);
}

_bundle.close(bundleId);
}

Expand Down
28 changes: 0 additions & 28 deletions contracts/test/TestSet.sol

This file was deleted.

55 changes: 55 additions & 0 deletions tests/test_basicriskpool_bundle_allocation_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,58 @@ def test_bundle_allocation_with_three_equal_bundles(
) = bundle

assert 3000 == lockedCapital


def test_failing_bundle_allocation(
instance: GifInstance,
testCoin,
gifTestProduct: GifTestProduct,
riskpoolKeeper: Account,
owner: Account,
customer: Account,
feeOwner: Account,
capitalOwner: Account
):
num_bundles = 2
product = gifTestProduct.getContract()
riskpool = gifTestProduct.getRiskpool().getContract()
riskpool.setMaximumNumberOfActiveBundles(num_bundles, {'from': riskpoolKeeper})
instanceService = instance.getInstanceService()

initialFunding = 1000

# fund the riskpools
for _ in range(num_bundles):
fund_riskpool(instance, owner, capitalOwner, riskpool, riskpoolKeeper, testCoin, initialFunding)

# create minimal policy application
premium = 100
sumInsured = 1200
metaData = bytes(0)
applicationData = bytes(0)

testCoin.transfer(customer, premium, {'from': owner})
testCoin.approve(instance.getTreasury(), premium, {'from': customer})

tx = product.applyForPolicy(
premium,
sumInsured,
metaData,
applicationData,
{'from': customer})

processId = tx.return_value

print('processId {}'.format(processId))
print(tx.info())

assert 'LogRiskpoolCollateralizationFailed' in tx.events
assert len(tx.events['LogRiskpoolCollateralizationFailed']) == 1

failedEvent = tx.events['LogRiskpoolCollateralizationFailed'][0]
assert failedEvent['processId'] == processId
assert failedEvent['amount'] == sumInsured

application = instanceService.getApplication(processId)
applicationState = application[0]
assert applicationState == 0 # enum ApplicationState {Applied, Revoked, Underwritten, Declined}
4 changes: 2 additions & 2 deletions tests/test_bundle_create_use_burn.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,10 @@ def test_close_and_burn_bundle(
assert testCoin.balanceOf(bundleOwner) == bundleOwnerBefore + netWithdrawalAmount

# check that close results in blocking all other actions on the bundle
with brownie.reverts('ERROR:POL-042:NO_ACTIVE_BUNDLES'):
with brownie.reverts('ERROR:BUC-052:CLOSED_INVALID_TRANSITION'):
riskpool.closeBundle(bundleId, {'from': bundleOwner})

with brownie.reverts('ERROR:POL-042:NO_ACTIVE_BUNDLES'):
with brownie.reverts('ERROR:POL-044:BUNDLE_ID_NOT_IN_SET'):
riskpool.lockBundle(bundleId, {'from': bundleOwner})

with brownie.reverts('ERROR:BUC-052:CLOSED_INVALID_TRANSITION'):
Expand Down
44 changes: 37 additions & 7 deletions tests/test_riskpool_active_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,35 +41,49 @@ def test_create_bundle_max_active(
testCoin.transfer(riskpoolKeeper, 10 * initialFunding, {'from': owner})
testCoin.approve(instance.getTreasury(), 10 * initialFunding, {'from': riskpoolKeeper})

riskpool.bundles() == 1
assert riskpool.bundles() == 1
assert riskpool.activeBundles() == 1
assert instanceService.activeBundles(riskpoolId) == 1

bundle1 = riskpool.getBundle(0)

# ensure creation of another bundle is not allowed (max active bundles is 1 by default)
with brownie.reverts("ERROR:POL-041:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
with brownie.reverts("ERROR:POL-043:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
riskpool.createBundle(
bytes(0),
initialFunding,
{'from': riskpoolKeeper})

riskpool.closeBundle(bundle1[0], {'from': riskpoolKeeper})

assert riskpool.bundles() == 1
assert riskpool.activeBundles() == 0
assert instanceService.activeBundles(riskpoolId) == 0

riskpool.createBundle(
bytes(0),
initialFunding,
{'from': riskpoolKeeper})

assert riskpool.bundles() == 2
assert riskpool.activeBundles() == 1
assert instanceService.activeBundles(riskpoolId) == 1

# ensure a seconds bundle can be added when setting max active bundles to 2
riskpool.setMaximumNumberOfActiveBundles(2, {'from': riskpoolKeeper})
riskpool.createBundle(
bytes(0),
initialFunding,
{'from': riskpoolKeeper})

riskpool.bundles() == 2
assert riskpool.bundles() == 3
assert riskpool.activeBundles() == 2
assert instanceService.activeBundles(riskpoolId) == 2

bundle2 = riskpool.getBundle(1)
bundle3 = riskpool.getBundle(1)
bundle3 = riskpool.getBundle(2)

with brownie.reverts("ERROR:POL-041:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
with brownie.reverts("ERROR:POL-043:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
riskpool.createBundle(
bytes(0),
initialFunding,
Expand All @@ -78,20 +92,36 @@ def test_create_bundle_max_active(
# ensure another bundle can be created only after locking one bundle
riskpool.lockBundle(bundle2[0], {'from': riskpoolKeeper})

assert riskpool.bundles() == 3
assert riskpool.activeBundles() == 1
assert instanceService.activeBundles(riskpoolId) == 1

riskpool.createBundle(
bytes(0),
initialFunding,
{'from': riskpoolKeeper})

assert riskpool.bundles() == 4
assert riskpool.activeBundles() == 2
assert instanceService.activeBundles(riskpoolId) == 2

# ensure locked bundle cannot be unlocked while max active bundles are in use
with brownie.reverts("ERROR:POL-041:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
with brownie.reverts("ERROR:POL-043:MAXIMUM_NUMBER_OF_ACTIVE_BUNDLES_REACHED"):
riskpool.unlockBundle(bundle2[0], {'from': riskpoolKeeper})

riskpool.closeBundle(bundle2[0], {'from': riskpoolKeeper})
riskpool.closeBundle(bundle3[0], {'from': riskpoolKeeper})

assert riskpool.bundles() == 4
assert riskpool.activeBundles() == 1
assert instanceService.activeBundles(riskpoolId) == 1

# ensure bundles can be created after closing one more bundle
riskpool.createBundle(
bytes(0),
initialFunding,
{'from': riskpoolKeeper})

assert riskpool.bundles() == 5
assert riskpool.activeBundles() == 2
assert instanceService.activeBundles(riskpoolId) == 2