Skip to content

Commit

Permalink
Merge pull request #21 from etherisc/bugfix/policy-checks-product
Browse files Browse the repository at this point in the history
Add new modifier to check if productId belongs to component
  • Loading branch information
doerfli authored Aug 15, 2022
2 parents d58521a + f11d70b commit 3a5ade7
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 21 deletions.
27 changes: 21 additions & 6 deletions contracts/examples/AyiiProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";

import "@etherisc/gif-interface/contracts/components/Product.sol";
import "../modules/PolicyController.sol";

import "../modules/AccessController.sol";

contract AyiiProduct is
Product,
AccessControl
AccessControl,
Initializable
{
bytes32 public constant NAME = "AreaYieldIndexProduct";
bytes32 public constant VERSION = "0.1";
Expand Down Expand Up @@ -40,9 +44,10 @@ contract AyiiProduct is
}

uint256 private _oracleId;
PolicyController private _policy;

mapping(bytes32 /* riskId */ => Risk) private _risks;
mapping(bytes32 /* riskId */ => bytes32 [] /*policyIds*/) private _policies;
mapping(bytes32 /* riskId */ => bytes32 [] /* processIds */) private _policies;
bytes32 [] private _applications; // useful for debugging, might need to get rid of this

event LogAyiiPolicyCreated(bytes32 policyId, address policyHolder, uint256 premiumAmount, uint256 sumInsuredAmount);
Expand Down Expand Up @@ -70,6 +75,10 @@ contract AyiiProduct is
_setupRole(INSURER_ROLE, insurer);
}

function initialize(address registry) public initializer {
_policy = PolicyController(_getContractAddress("Policy"));
}

function createRisk(
bytes32 projectId,
bytes32 uaiId,
Expand Down Expand Up @@ -158,12 +167,12 @@ contract AyiiProduct is
}
}

function triggerOracle(bytes32 riskId)
function triggerOracle(bytes32 processId)
external
onlyRole(INSURER_ROLE)
returns(uint256 requestId)
{
Risk storage risk = _risks[riskId];
Risk storage risk = _risks[_getRiskId(processId)];
require(risk.createdAt > 0, "ERROR:AYI-010:RISK_UNDEFINED");
require(risk.requestId == 0, "ERROR:AYI-011:ORACLE_ALREADY_TRIGGERED");

Expand All @@ -174,7 +183,7 @@ contract AyiiProduct is
);

requestId = _request(
riskId,
processId,
queryData,
"oracleCallback",
_oracleId
Expand All @@ -193,7 +202,7 @@ contract AyiiProduct is

function oracleCallback(
uint256 requestId,
bytes32 riskId,
bytes32 processId,
bytes calldata responseData
)
external
Expand All @@ -206,6 +215,7 @@ contract AyiiProduct is
uint256 aaay
) = abi.decode(responseData, (bytes32, bytes32, bytes32, uint256));

bytes32 riskId = _getRiskId(processId);
require(riskId == getRiskId(projectId, uaiId, cropId), "ERROR:AYI-020:RISK_ID_MISMATCH");

Risk storage risk = _risks[riskId];
Expand Down Expand Up @@ -354,4 +364,9 @@ contract AyiiProduct is
function getApplicationDataStructure() external override view returns(string memory dataStructure) {
return "(bytes32 riskId)";
}

function _getRiskId(bytes32 processId) private view returns(bytes32 riskId) {
IPolicy.Application memory application = _getApplication(processId);
(riskId) = abi.decode(application.data, (bytes32));
}
}
42 changes: 36 additions & 6 deletions contracts/flows/PolicyDefaultFlow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,26 @@ contract PolicyDefaultFlow is
PolicyController policy = getPolicyContract();
require(
policy.getPolicy(processId).state == IPolicy.PolicyState.Expired,
"ERROR:PFD-001:POLICY_NOT_EXPIRED"
"ERROR:PFD-002:POLICY_NOT_EXPIRED"
);
_;
}

modifier onlyResponsibleProduct(bytes32 processId) {
PolicyController policy = getPolicyContract();
IPolicy.Metadata memory metadata = policy.getMetadata(processId);
ComponentController component = ComponentController(getContractFromRegistry("Component"));
require(metadata.productId == component.getComponentId(address(msg.sender)), "ERROR:PFD-003:PROCESSID_PRODUCT_MISMATCH");
_;
}

// ComponentController private _component;

// solhint-disable-next-line no-empty-blocks
constructor(address _registry)
WithRegistry(_registry)
{ }
{
}

function newApplication(
address owner,
Expand All @@ -73,6 +84,7 @@ contract PolicyDefaultFlow is

function revoke(bytes32 processId)
external
onlyResponsibleProduct(processId)
{
IPolicy policy = getPolicyContract();
policy.revokeApplication(processId);
Expand All @@ -81,6 +93,7 @@ contract PolicyDefaultFlow is
/* success implies the successful creation of a policy */
function underwrite(bytes32 processId)
external
onlyResponsibleProduct(processId)
returns(bool success)
{
// attempt to get the collateral to secure the policy
Expand All @@ -103,6 +116,7 @@ contract PolicyDefaultFlow is
*/
function collectPremium(bytes32 processId, uint256 amount)
public
onlyResponsibleProduct(processId)
returns(
bool success,
uint256 feeAmount,
Expand All @@ -122,14 +136,18 @@ contract PolicyDefaultFlow is
}
}

function decline(bytes32 processId) external {
function decline(bytes32 processId)
onlyResponsibleProduct(processId)
external
{
IPolicy policy = getPolicyContract();
policy.declineApplication(processId);
}

function expire(bytes32 processId)
external
onlyActivePolicy(processId)
onlyResponsibleProduct(processId)
{
IPolicy policy = getPolicyContract();
policy.expirePolicy(processId);
Expand All @@ -138,6 +156,7 @@ contract PolicyDefaultFlow is
function close(bytes32 processId)
external
onlyExpiredPolicy(processId)
onlyResponsibleProduct(processId)
{
IPolicy policy = getPolicyContract();
policy.closePolicy(processId);
Expand All @@ -153,6 +172,7 @@ contract PolicyDefaultFlow is
)
external
onlyActivePolicy(processId)
onlyResponsibleProduct(processId)
returns (uint256 claimId)
{
claimId = getPolicyContract().createClaim(
Expand All @@ -166,18 +186,25 @@ contract PolicyDefaultFlow is
uint256 claimId,
uint256 confirmedAmount
)
external
external
onlyResponsibleProduct(processId)
{
PolicyController policy = getPolicyContract();
policy.confirmClaim(processId, claimId, confirmedAmount);
}

function declineClaim(bytes32 processId, uint256 claimId) external {
function declineClaim(bytes32 processId, uint256 claimId)
external
onlyResponsibleProduct(processId)
{
PolicyController policy = getPolicyContract();
policy.declineClaim(processId, claimId);
}

function closeClaim(bytes32 processId, uint256 claimId) external {
function closeClaim(bytes32 processId, uint256 claimId)
external
onlyResponsibleProduct(processId)
{
PolicyController policy = getPolicyContract();
policy.closeClaim(processId, claimId);
}
Expand All @@ -189,6 +216,7 @@ contract PolicyDefaultFlow is
bytes calldata data
)
external
onlyResponsibleProduct(processId)
returns(uint256 payoutId)
{
payoutId = getPolicyContract()
Expand All @@ -200,6 +228,7 @@ contract PolicyDefaultFlow is
uint256 payoutId
)
external
onlyResponsibleProduct(processId)
returns(
bool success,
uint256 feeAmount,
Expand All @@ -225,6 +254,7 @@ contract PolicyDefaultFlow is
uint256 _responsibleOracleId
)
external
onlyResponsibleProduct(processId)
returns (uint256 _requestId)
{
_requestId = getQueryContract().request(
Expand Down
12 changes: 6 additions & 6 deletions contracts/modules/PolicyController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@ contract PolicyController is
// bytes32 public constant NAME = "PolicyController";

// Metadata
mapping(bytes32 => Metadata) public metadata;
mapping(bytes32 /* processId */ => Metadata) public metadata;

// Applications
mapping(bytes32 => Application) public applications;
mapping(bytes32 /* processId */ => Application) public applications;

// Policies
mapping(bytes32 => Policy) public policies;
mapping(bytes32 /* processId */ => Policy) public policies;

// TODO decide for current data structure or alternative
// alternative mapping(bytes32 => Claim [])
// Claims
mapping(bytes32 => mapping(uint256 => Claim)) public claims;
mapping(bytes32 /* processId */ => mapping(uint256 /* claimId */ => Claim)) public claims;

// TODO decide for current data structure or alternative
// alternative mapping(bytes32 => Payout [])
// Payouts
mapping(bytes32 => mapping(uint256 => Payout)) public payouts;
mapping(bytes32 => uint256) public payoutCount;
mapping(bytes32 /* processId */ => mapping(uint256 /* payoutId */ => Payout)) public payouts;
mapping(bytes32 /* processId */ => uint256) public payoutCount;

// counter for assigned processIds, used to ensure unique processIds
uint256 private _assigendProcessIds;
Expand Down
4 changes: 2 additions & 2 deletions tests/test_ayii_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ def test_happy_path(

print('--- step trigger oracle (call chainlin node) -------------')

tx[0] = product.triggerOracle(riskId[0])
tx[1] = product.triggerOracle(riskId[1])
tx[0] = product.triggerOracle(policyId[0])
tx[1] = product.triggerOracle(policyId[1])
requestId = [tx[0].return_value, tx[1].return_value]

# ensure event emitted as chainlink client
Expand Down
2 changes: 1 addition & 1 deletion tests/test_process_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def test_process_apply_underwrite_expire_close(
with brownie.reverts('ERROR:POC-019:APPLICATION_STATE_INVALID'):
product.decline(processId, {'from': productOwner})

with brownie.reverts('ERROR:PFD-001:POLICY_NOT_EXPIRED'):
with brownie.reverts('ERROR:PFD-002:POLICY_NOT_EXPIRED'):
product.close(processId, {'from': productOwner})

product.expire(processId, {'from': productOwner})
Expand Down

0 comments on commit 3a5ade7

Please sign in to comment.