Skip to content

Instantly share code, notes, and snippets.

@heri16
Created Jun 21, 2021
Embed
What would you like to do?
WIP: Solidity Object Structures for Insured Finance

Enums

  enum CoverType { SMART_PROTOCOL_FAILURE, STABLECOIN_DEVALUATION, CUSTODIAN_FAILURE, RUGPULL_LIQUIDITY_SCAM }
  enum CurrencyType { USDT, DAI, USDC }
  enum InsuredSumRule { PARTIAL, FULL }
  // enum ChainType { NON_EVM , EVM }

CoinId Decimals & CurrencyDecimals

  mapping(CurrencyType => uint8) currencyDecimals;
  mapping(string => uint8) coinIdToDecimals;

CoverRequest

  // For passing parameter and store state variables
  CoverRequest[] requests; // CoverRequest.id
  mapping(address => uint[]) buyerToRequests;
  mapping(string => uint[]) coinIdToRequests;
  struct CoverRequest {
    uint coverQty; // coverQty decimals depends on coinIdToDecimals mapping
    uint8 coverMonths; // represent month value 1-12
    uint insuredSum;

    uint insuredSumTarget; // if full funding : insuredSum - 2$
    CurrencyType insuredSumCurrency;
    uint premiumSum;
    CurrencyType premiumCurrency;
    uint expiredAt; // now + 14 days
    string coinId; // CoinGecko
    CoverLimit coverLimit;

    InsuredSumRule insuredSumRule;

    address buyer;
  }

CoverOffer

  // For passing parameter and store state variables
  CoverOffer[] offers; // CoverOffer.id
  mapping(address => uint[]) funderToOffers;
  mapping(string => uint[]) coinIdToOffers;
  struct CoverOffer {
    uint8 minCoverMonths; // represent month value 1-12 (expiredAt + 1 month - now >= minCoverMonths)
    uint insuredSum;
    CurrencyType insuredSumCurrency;
    uint premiumCostPerMonth; // $0.02 per $1 insured per Month (2000)
    CurrencyType premiumCurrency;
    // IMPORTANT: max date for buying cover = expiredAt + 1 month
    uint expiredAt; // despositEndDate - 14 days beforeDepositEndDate
    string coinId; // CoinGecko
    CoverLimit coverLimit;

    uint insuredSumRemaining;
    InsuredSumRule insuredSumRule;

    address funder; // address which created the listing
  }

Cover Limit

  struct CoverLimit {
    CoverType coverType;
    uint[] teritoryIds; // Platform Id, Price Feed Id, Custodian Id , (Dex Pool Id not Yet implemented)
  }

Cover / Insurance

  // Storage struct
  // Relationship: CoverCoverOffer ||--< Cover
  // Relationship: CoverRequest ||--< Cover
  // Relationship: One cover can have only one offer
  // Relationship: One cover can have only one request
  InsuranceCover[] covers; // InsuranceCover.id
  mapping(address => uint[]) holderToCovers;
  mapping(uint => uint[]) offerIdToCovers;
  mapping(uint => uint[]) requestIdToCovers;
  struct InsuranceCover {
    // type computed from (offerId != 0) or (requestId != 0)

    // If BuyCover (take offer)
    uint offerId; // from BuyCover.offerId

    // If CoverFunding (take request)
    uint requestId; // from CoverFunding.requestId
    // uint[] provideIds;

    // will validate claimSender
    address holder; // from BuyCover.buyer or CoverRequest.buyer

    // will validate maximum claimSum
    uint insuredSum; // from BuyCover.insuredSum or sum(CoverFunding.fundingSum)

    // will validate maximum claimQuantity
    uint coverQty; // from BuyCover.coverQty or CoverRequest.coverQty

    // will validate claimDeadline
    uint endAt;
    // if take offer, cover starts immediately
    // endAt = now + BuyCover.coverMonths
    // if take request, and fully funded before expired, cover starts immediately...
    // endAt = now + CoverRequest.coverMonths
    // if take request, and expired before fully funded, cover starts on CoverRequest.expiredAt
    // if (endAt == 0 && now > CoverRequest.expiredAt) endAt = expiredAt + CoverRequest.coverMonths
  }

Take Request

  // Storage: "Booking" object when take request
  // Relationship: CoverRequest ||--< CoverFunding
  mapping(uint => CoverFunding[]) requestIdToCoverFundings;
  mapping(address => CoverFunding[]) funderToCoverFundings;
  struct CoverFunding {
    uint requestId;
    address funder;

    // insurance data:
    uint fundingSum; // part or portion of total insuredSum
  }

  // Payload: object when take request
  // Virtual struct/type for payload (type of payloadBuyCover)
  struct ProvideCover {
    uint requestId;
    address provider;

    // insurance data:
    uint fundingSum;

    CoinPricingInfo assetPriceInfo;
    CollectPermit assetPermit;
  }

Take Offer

  // Payload: object when take offer
  // Virtual struct/type for payload (type of payloadBuyCover)
  struct BuyCover {
    uint offerId;
    address buyer;

    // insurance data:
    uint8 coverMonths; // represent month value 1-12
    uint coverQty; // coverQty decimals depends on coinIdToDecimals mapping
    uint insuredSum; // need validation : coverQty * assetPriceInfo.coinCurrentPrice

    CoinPricingInfo assetPriceInfo;
    CollectPermit premiumPermit;
    // TODO: validate coverQty based on insuredSum
  }

Coin Pricing Info

  struct CoinPricingInfo {
    string coinId;
    string coinSymbol;
    uint lastUpdatedAt;
    uint coinCurrentPrice; // decimals 6
    uint8 sigV; // EIP712 Signature
    bytes32 sigR;
    bytes32 sigS;
  }

Platforms

  Platform[] platforms;
  struct Platform {
    string name;
    string website;
  }

Oracles

  Oracle[] oracles;
  struct Oracle {
    string name;
    string website;
  }

Price Feed

  PriceFeed[] usdPriceFeeds;
  mapping(string => uint[]) symbolToUsdPriceFeeds;
  struct PriceFeed {
    uint oracleId;
    uint chainId;
    uint8 decimals;
    address proxyAddress;
  }

Custodian

  Custodian[] custodians;
  struct Custodian {
    string name;
    string website;
  }

Collect Permit (EIP2612)

  struct EIP2612Permit {
    address owner;
    uint value;
    address spender;
    uint deadline;
    uint8 sigV; // EIP712 Signature
    bytes32 sigR;
    bytes32 sigS;
  }

Payable Data (EIP1363)

See: https://github.com/fx-portal/contracts/blob/main/contracts/examples/erc20-transfer/FxERC20ChildTunnel.sol#L59

  struct EIP1363Data {
    bytes32 payType;
    bytes payData;
  }

  bytes32 public constant CREATE_COVER_REQUEST = keccak256("CREATE_COVER_REQUEST");
  bytes32 public constant CREATE_COVER_OFFER = keccak256("CREATE_COVER_OFFER");

  function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4) {
    // decode incoming data
    (bytes32 payType, bytes memory payData) = abi.decode(data, (bytes32, bytes));

    if (payType == CREATE_COVER_REQUEST) {
      _createCoverRequest(operator, from, value, payData);
    } else if (payType == CREATE_COVER_OFFER) {
      _createCoverOffer(operator, from, value, payData);
    } else {
      revert("ERC1363Receiver: INVALID_PAY_TYPE");
    }
  }

  function _createCoverRequest(address operator, address from, uint256 value, bytes memory payData) internal {
    (CreateCoverRequestData memory data) = abi.decode(payData, (CreateCoverRequestData));
  }

  function _createCoverOffer(address operator, address from, uint256 value, bytes memory payData) internal {
    (CreateCoverOfferData memory data) = abi.decode(payData, (CreateCoverOfferData));
  }

Cover Request Payload

  struct CreateCoverRequestData {
    CoverRequest request; //
    CoinPricingInfo assetPricing; //
    CoinPricingInfo feePricing; //
    EIP2612Permit collectPremiumPermit; // for transfer DAI, USDT, USDC
  }

Cover Offer Payload

  struct CreateCoverOfferData {
    CoverOffer offer; //
    CoinPricingInfo assetPricing; //
    CoinPricingInfo feePricing; //
    EIP2612Permit collectFundingPermit; // for transfer DAI, USDT, USDC
  }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment