Skip to content

Commit

Permalink
Merge pull request #744 from etherisc/feature/flight-amend-oracle
Browse files Browse the repository at this point in the history
Feature/flight amend oracle
  • Loading branch information
matthiaszimmermann authored Oct 9, 2024
2 parents bd6db59 + e0ca318 commit bc9951f
Show file tree
Hide file tree
Showing 15 changed files with 785 additions and 365 deletions.
73 changes: 55 additions & 18 deletions contracts/examples/flight/FlightLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,27 @@ import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId} from "../../type/NftId.sol";
import {RequestId} from "../../type/RequestId.sol";
import {RiskId, RiskIdLib} from "../../type/RiskId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";


library FlightLib {

event LogFlightProductErrorUnprocessableStatus(RequestId requestId, RiskId riskId, bytes1 status);
event LogFlightProductErrorUnexpectedStatus(RequestId requestId, RiskId riskId, bytes1 status, int256 delayMinutes);

error ErrorFlightProductRiskInvalid(RiskId riskId);
error ErrorFlightProductPremiumAmountTooSmall(Amount premiumAmount, Amount minPremium);
error ErrorFlightProductPremiumAmountTooLarge(Amount premiumAmount, Amount maxPremium);
error ErrorFlightProductArrivalBeforeDepartureTime(Timestamp departureTime, Timestamp arrivalTime);
error ErrorFlightProductArrivalAfterMaxFlightDuration(Timestamp arrivalTime, Timestamp maxArrivalTime, Seconds maxDuration);
error ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds minTimeBeforeDeparture);
error ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds maxTimeBeforeDeparture);
error ErrorFlightProductNotEnoughObservations(uint256 observations, uint256 minObservations);
error ErrorFlightProductClusterRisk(Amount totalSumInsured, Amount maxTotalPayout);

function checkApplicationData(
FlightProduct flightProduct,
Str flightData,
Expand Down Expand Up @@ -45,32 +59,45 @@ library FlightLib {

// solhint-disable
if (premiumAmount < flightProduct.MIN_PREMIUM()) {
revert FlightProduct.ErrorFlightProductPremiumAmountTooSmall(premiumAmount, flightProduct.MIN_PREMIUM());
revert ErrorFlightProductPremiumAmountTooSmall(premiumAmount, flightProduct.MIN_PREMIUM());
}
if (premiumAmount > flightProduct.MAX_PREMIUM()) {
revert FlightProduct.ErrorFlightProductPremiumAmountTooLarge(premiumAmount, flightProduct.MAX_PREMIUM());
revert ErrorFlightProductPremiumAmountTooLarge(premiumAmount, flightProduct.MAX_PREMIUM());
}
if (arrivalTime <= departureTime) {
revert FlightProduct.ErrorFlightProductArrivalBeforeDepartureTime(departureTime, arrivalTime);
revert ErrorFlightProductArrivalBeforeDepartureTime(departureTime, arrivalTime);
}

// test mode allows the creation for policies that ore not time constrained
if (!testMode && arrivalTime > departureTime.addSeconds(flightProduct.MAX_FLIGHT_DURATION())) {
revert FlightProduct.ErrorFlightProductArrivalAfterMaxFlightDuration(arrivalTime, departureTime, flightProduct.MAX_FLIGHT_DURATION());
if (arrivalTime > departureTime.addSeconds(flightProduct.MAX_FLIGHT_DURATION())) {
revert ErrorFlightProductArrivalAfterMaxFlightDuration(arrivalTime, departureTime, flightProduct.MAX_FLIGHT_DURATION());
}

// test mode allows the creation for policies that are outside restricted policy creation times
if (!testMode && departureTime < TimestampLib.current().addSeconds(flightProduct.MIN_TIME_BEFORE_DEPARTURE())) {
revert FlightProduct.ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MIN_TIME_BEFORE_DEPARTURE());
revert ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MIN_TIME_BEFORE_DEPARTURE());
}
if (!testMode && departureTime > TimestampLib.current().addSeconds(flightProduct.MAX_TIME_BEFORE_DEPARTURE())) {
revert FlightProduct.ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MAX_TIME_BEFORE_DEPARTURE());
revert ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MAX_TIME_BEFORE_DEPARTURE());
}
// solhint-enable
}


function checkClusterRisk(
Amount sumOfSumInsuredAmounts,
Amount sumInsuredAmount,
Amount maxTotalPayout
)
public
pure
{
if (sumOfSumInsuredAmounts + sumInsuredAmount > maxTotalPayout) {
revert ErrorFlightProductClusterRisk(sumOfSumInsuredAmounts + sumInsuredAmount, maxTotalPayout);
}
}


/// @dev calculates payout option based on flight status and delay minutes.
/// Is not a view function as it emits log evens in case of unexpected status.
// TODO decide if reverts instead of log events could work too (and convert the function into a view function)
function checkAndGetPayoutOption(
RequestId requestId,
RiskId riskId,
Expand All @@ -85,13 +112,13 @@ library FlightLib {

// check status
if (status != "L" && status != "A" && status != "C" && status != "D") {
emit FlightProduct.LogErrorUnprocessableStatus(requestId, riskId, status);
emit LogFlightProductErrorUnprocessableStatus(requestId, riskId, status);
return payoutOption;
}

if (status == "A") {
// todo: active, reschedule oracle call + 45 min
emit FlightProduct.LogErrorUnexpectedStatus(requestId, riskId, status, delayMinutes);
emit LogFlightProductErrorUnexpectedStatus(requestId, riskId, status, delayMinutes);
return payoutOption;
}

Expand All @@ -114,7 +141,7 @@ library FlightLib {
{
// check we have enough observations
if (statistics[0] < flightProduct.MIN_OBSERVATIONS()) {
revert FlightProduct.ErrorFlightProductNotEnoughObservations(statistics[0], flightProduct.MIN_OBSERVATIONS());
revert ErrorFlightProductNotEnoughObservations(statistics[0], flightProduct.MIN_OBSERVATIONS());
}

weight = 0;
Expand Down Expand Up @@ -147,10 +174,10 @@ library FlightLib {
)
{
if (premium < flightProduct.MIN_PREMIUM()) {
revert FlightProduct.ErrorFlightProductPremiumAmountTooSmall(premium, flightProduct.MIN_PREMIUM());
revert ErrorFlightProductPremiumAmountTooSmall(premium, flightProduct.MIN_PREMIUM());
}
if (premium > flightProduct.MAX_PREMIUM()) {
revert FlightProduct.ErrorFlightProductPremiumAmountTooLarge(premium, flightProduct.MAX_PREMIUM());
revert ErrorFlightProductPremiumAmountTooLarge(premium, flightProduct.MAX_PREMIUM());
}

sumInsuredAmount = AmountLib.zero();
Expand Down Expand Up @@ -186,7 +213,8 @@ library FlightLib {
(exists, flightRisk) = getFlightRisk(
reader,
productNftId,
riskId);
riskId,
false);

statusAvailable = flightRisk.statusUpdatedAt.gtz();
payoutOption = flightRisk.payoutOption;
Expand All @@ -201,6 +229,10 @@ library FlightLib {
pure
returns (Amount payoutAmount)
{
if (payoutOption == type(uint8).max) {
return AmountLib.zero();
}

// retrieve payout amounts from application data
(, Amount[5] memory payoutAmounts) = abi.decode(
applicationData, (Amount, Amount[5]));
Expand Down Expand Up @@ -228,7 +260,7 @@ library FlightLib {
)
{
riskId = getRiskId(productNftId, flightData);
(exists, flightRisk) = getFlightRisk(reader, productNftId, riskId);
(exists, flightRisk) = getFlightRisk(reader, productNftId, riskId, false);

// create new risk if not existing
if (!exists) {
Expand All @@ -250,7 +282,8 @@ library FlightLib {
function getFlightRisk(
InstanceReader reader,
NftId productNftId,
RiskId riskId
RiskId riskId,
bool requireRiskExists
)
public
view
Expand All @@ -262,6 +295,10 @@ library FlightLib {
// check if risk exists
exists = reader.isProductRisk(productNftId, riskId);

if (!exists && requireRiskExists) {
revert ErrorFlightProductRiskInvalid(riskId);
}

// get risk data if risk exists
if (exists) {
flightRisk = abi.decode(
Expand Down
30 changes: 28 additions & 2 deletions contracts/examples/flight/FlightOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ pragma solidity ^0.8.20;

import {IAuthorization} from "../../authorization/IAuthorization.sol";

import {FULFILLED} from "../../type/StateId.sol";
import {ACTIVE, FULFILLED, FAILED} from "../../type/StateId.sol";
import {NftId} from "../../type/NftId.sol";
import {BasicOracle} from "../../oracle/BasicOracle.sol";
import {RequestId} from "../../type/RequestId.sol";
import {LibRequestIdSet} from "../../type/RequestIdSet.sol";
import {RiskId} from "../../type/RiskId.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp} from "../../type/Timestamp.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";

contract FlightOracle is
BasicOracle
Expand Down Expand Up @@ -110,6 +111,7 @@ contract FlightOracle is
}
}


//--- view functions ----------------------------------------------------//

// TODO decide if the code below should be moved to GIF
Expand All @@ -131,6 +133,30 @@ contract FlightOracle is
return LibRequestIdSet.getElementAt(_activeRequests, idx);
}


function getRequestState(RequestId requestId)
external
view
returns (
RiskId riskId,
string memory flightData,
StateId requestState,
bool readyForResponse,
bool waitingForResend
)
{
bytes memory requestData = _getInstanceReader().getRequestInfo(requestId).requestData;
Str fltData;
Timestamp departureTime;
(riskId, fltData, departureTime) = abi.decode(requestData, (RiskId, Str, Timestamp));

flightData = fltData.toString();
requestState = _getInstanceReader().getRequestState(requestId);
readyForResponse = requestState == ACTIVE() && TimestampLib.current() >= departureTime;
waitingForResend = requestState == FAILED();
}


function decodeFlightStatusRequestData(bytes memory data) external pure returns (FlightStatusRequest memory) {
return abi.decode(data, (FlightStatusRequest));
}
Expand Down
Loading

0 comments on commit bc9951f

Please sign in to comment.