Skip to content

Commit

Permalink
Merge pull request #56 from etherisc/feature/replace-idset-with-oz-en…
Browse files Browse the repository at this point in the history
…umerableset

Feature/replace idset with oz enumerableset
  • Loading branch information
doerfli committed Sep 8, 2022
2 parents 9de05bb + 4d2b14c commit 96bb3e2
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 228 deletions.
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

Loading

0 comments on commit 96bb3e2

Please sign in to comment.