Cover Portal - Smart Contract Audit Report

Audit Summary

Cover Portal Audit Report Uno Re is building a set of smart contracts to faciliate a single-sided insurance and reinsurance platform with opportunities to stake funds and earn rewards.

For this audit, we reviewed the following contracts on the team's private GitHub repository:

We previously reviewed the project team's SSRP platform here.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: January 27th, 2022.
Updated: February 15th, 2022 to include changes made to the PremiumPool contract which allow more flexibility in providing SSIP rewards.
Updated: April 18th, 2022 to include changes made to select contracts implementing SCR check.
Updated: May 7th, 2022 to include changes made the SSIP contract to fix rounding error in calculation of available withdraw amount.

Contracts Overview

  • The contracts utilize ReentrancyGuard to prevent against re-entrancy attacks in applicable functions.
  • As the contracts are implemented using Solidity v0.8.0, they are safe from any possible overflows/underflows.
  • The team must exercise caution when assigning the staking token to avoid using fee-on-transfer tokens.
ExchangeAgent Contract:
  • This contract is used throughout the protocol to facilitate token swaps as needed.
  • Only whitelisted addresses are able to use this contract to convert tokens to ETH, ETH to tokens, or tokens to tokens.
  • Upon swapping tokens, the user must provide an approval for the contract to use their tokens.
  • Upon swapping ETH, the contract uses its own balance to support the swap, so the team must separately transfer the desired amount of ETH to the contract in advance. The team should consider making the convertForToken() function payable so that the ETH can be provided when calling the function.
  • The contract uses a TWAP Oracle Price Feed for time-weighted average pricing; The TWAP was not provided to us in the scope of the audit, so we are unable to provide an assessment in terms of security.
  • The owner can adjust the slippage used while swapping to any value at any time.
  • The owner can add or remove any address from the whitelist at any time.
PremiumPool Contract:
  • This contract is used by the project team to manage the platform's collected premium payments.
  • Whitelisted users can use the collectPremiumInETH() and collectPremium() functions to deposit ETH or any token accepted by the platform and allocate it as follows:
    • 70% is allocated towards SSIP rewards.
    • 20% is allocated towards the Buyback functionality.
    • 10% is allocated towards SSRP rewards.
  • The owner can use this contract to exchange the ETH accumulated for SSRP rewards for USDC via the ExchangeAgent contract and transfer the returned USDC to the rewarder address supplied as an input parameter.
  • The owner can transfer any amount of ETH or tokens accumulated for SSIP rewards to any rewarder address supplied as an input parameter, as long as the address is a contract.
  • The owner can use this contract to exchange the ETH or tokens accumulated for the Buyback for UNO tokens via the ExchangeAgent contract and transfer the returned UNO tokens to the 0x..dead address.
  • The owner can use the withdrawPremium() function to withdraw any ETH or tokens from this contract at any time; the team has communicated that this is only intended to be used during an emergency, and has acknowledged that in the event that this function is executed, the functionality in the contract as described above will no longer be operational.
  • The owner can add or remove any address as an accepted currency.
  • The owner can grant or revoke an allowance to address to spend any amount of tokens in this contract.
  • The owner can add or remove any address from the whitelist at any time.
RiskPoolFactory and RiskPool Contracts:
  • Anyone can use the RiskPoolFactory contract to create new RiskPool instances at any time, but it is intended that all RiskPool contracts will only be created by the project team.
  • Upon entering the RiskPool, users are minted an amount of shares based on the share price defined in the RiskPool contract. Initially, the price is 1 asset token for 1 share, but this is subject to change as tokens are removed from the Pool via a policy claim.
  • After a policy claim, the share price is recalculated based on the proportion of tokens in the contract to shares in circulation.
  • Users can transfer their available RiskPool tokens to any address, and the underlying LP is reassigned to the recipient as well.
  • In order for a user to exit their staked position in the RiskPool, users must submit a withdrawal request using the SingleSidedReinsurancePool contract and wait a 10 day lock period in order to withdraw their funds; rewards continue to accrue during the waiting period.
  • Once the wait time has elapsed, users can exchange their shares for an amount of the asset token based on the current share price.
  • Alternatively, users with a pending withdrawal request can use the migrate() function in the SingleSidedReinsurancePool contract to withdraw their staked assets once the wait time has elapsed.
  • Users must exercise caution when using the migrate() function as any remaining shares the user is holding will be burned and their underlying value will be sent to the Migration contract controlled by the project team.
  • In the event that there are not enough asset tokens to fulfill the withdrawal request, the contract will only transfer as much as it can provide, and the user's balance record will be eliminated from the system.
  • Users can cancel their withdrawal requests at any time.
SalesPolicyFactory Contract:
  • The owner can use this contract to deploy and manage a single SalesPolicy instance.
  • The owner can set the PremiumPool and ExchangeAgent contract addresses within the SalesPolicy contract at any time.
  • The owner can set the signer address and the deadline for buying a policy to any value at any time.
  • The owner can set the protocol URI value in the SalesPolicy to any value at any time.
  • The owner can add any token address as an approved premium currency in the Sales Policy contract at any time.
  • The owner can also use this contract to manage a list of Protocols; each protocol has an address and a blacklist status.
  • The owner can blacklist a Protocol at any time; blacklisted Protocols cannot be removed from the blacklist, but the owner can create a new Protocol with the same address, overwriting the previously stored data.
  • The owner can use this contract to control whether or not the blacklist is checked within the SalesPolicy contracts when users try to buy a policy for a specific Protocol.
  • The owner can set the CapitalAgent contract address used by the SalesPolicy to any address at any time.
SalesPolicy Contract:
  • Anyone can use this contract to buy purchase coverage offered by the platform.
  • Users are able to specify their own coverage amount, duration, and policy price for one or many Protocols.
  • The contract uses a signed message to ensure that the signer of the policy is the approved platform signer in order for a user to purchase the policy with the specified terms.
  • The signed message is only valid for a period of 7 days from the time it was signed.
  • Users can pay for the policy in ETH or any other accepted premium currency.
  • The contract uses the ExchangeAgent to convert the price of the policy from USDC to the given currency, after which the tokens are sent to the PremiumPool contract.
  • The contract may check each protocol requested by the user to see if it is blacklisted, if the project team has enabled this functionality.
  • The user may not receive coverage for blacklisted protocols.
  • The contract mints 1 ERC-721 NFT to the user for every policy purchased.
  • Once a policy has expired or has been claimed, the CapitalAgent contract may burn the NFT representing the policy at any time.
  • The contract uses the ERC-1776 Native Meta Transactions standard to allow users to interact with the contract without paying any gas fees.
CapitalAgent Contract:
  • This contract is used to manage the total capital staked within the SingleSidedInsurancePool and the total amount utilized by all the coverage policies in the SalesPolicy contract.
  • The contract maintains a minimum capital ratio (MCR) and solvency capital requirement (SCR), which is checked whenever a user attempts to unstake from the SSIP; The user can never withdraw more than a certain percentage of the total capital staked as determined by the team.
  • The contract also maintains a maximum leverage ratio (MLR), which dictates what percentage of the total capital staked can be utilized for policy coverage.
  • The MLR is checked whenever a user attempts to purchase coverage for a policy; the ratio of total utilized amount to total capital staked can never be greater than the MLR set by the team.
  • These restrictions do not apply to policy claims, although the total capital staked and the total utilized amount values are appropriately deducted.
  • The owner can add or remove any address from the Pool Whitelist at any time. Active SSIP pools are intended to be on this list.
  • The owner or any active pool can initialize any address as a Pool within the contract at any time.
  • The owner can remove any initialized Pools at any time, which deducts the pool's total capital from the contract's total capital staked value.
  • The owner can set the SalesPolicyFactory contract address to any value at any time.
  • The owner and the SalesPolicyFactory can initialize a Policy within the contract at any time.
  • The owner can remove the initialized Policy at any time, which sets the contract's total utilized amount to 0.
  • The Operator can set the MCR, MLR and SCR to any value at any time.
  • The owner can set the ExchangeAgent contract address and the Operator address to any address at any time.
RewarderFactory and Rewarder Contracts:
  • Anyone can use the RewarderFactory contract to create new Rewarder instances at any time, but it is intended that Rewarder contracts will only be created by the project team.
  • The Rewarder contract is used to facilitate staking rewards within the SyntheticSSIP, SyntheticSSRP, SingleSidedInsurancePool, and SingleSidedReinsurancePool contracts.
  • Only a single reward asset is supported by this contract.
  • Upon reward distribution, the SSIP or SSRP pool assigned to the Rewarder contract will trigger the onReward() function, which will attempt to transfer the requested amount of the reward token to the provided address.
  • Please note that the reward distribution depends on the balance of the Rewarder contract, and the transaction will still be successful even if there are not enough tokens in the Rewarder contract to fulfill the distribution.
  • The Operator address can withdraw any reward tokens from the contract at any time; note that any other token in the contract balance is not able to be withdrawn.
  • The Operator address can transfer the Operator Role to any address at any time.
SyntheticSSIPFactory/SyntheticSSRPFactory and SyntheticSSIP/SyntheticSSRP Contracts:
  • Anyone can use the Factory contract to create new SyntheticSSIP/SyntheticSSRP instances at any time, but it is intended that the pool contracts will only be created by the project team.
  • The pool contracts allow users to stake a single asset determined by the project team in exchange for rewards.
  • In order for a user to exit their staked position, users must submit a withdrawal request and wait a 10 day lock period in order to withdraw their funds; rewards continue to accrue during the waiting period.
  • Once the wait time has elapsed, users can also call the migrate() function to transfer their funds to the "migrateTo" address set by the project team. In the event that the user has a pending withdrawal request for which the wait time has elapsed, the pending withdraw amount is transferred to the user, and any remaining amount is transferred to the migrateTo address.
  • The migrate() function subsequently calls the onMigration() function on the Migration contract. This contract was not provided to us within the scope of this audit, so we are unable to provide an assessment in terms of security.
  • Users can cancel their withdrawal requests at any time.
  • Rewards are automatically calculated and transferred on deposits and withdraws, but any user may also choose to harvest their rewards manually at any time.
  • The owner can use the RewarderFactory to create a new Rewarder contract for the pool at any time.
  • The owner can set the reward rate, the migrateTo address, and the lock duration between withdrawals to any value at any time.
SingleSidedInsurancePool and SingleSidedReinsurancePool Contracts:
  • These contracts allow users to stake a single asset determined by the project team into a RiskPool configured by the project team in exchange for rewards in UNO tokens.
  • The funds deposited into the SSIP are managed by the CapitalAgent in order to meet the platform's minimum liquidity requirements in preparation for future policy claims.
  • In order for a user to exit their staked position, users must submit a withdrawal request and wait a lock period in order to withdraw their funds; rewards continue to accrue during the waiting period.
  • By default, the lock period is 10 days for the SSIP and 1 day for the SSRP.
  • Once the wait time has elapsed, users can also call the migrate() function to transfer their underlying assets in the Risk Pool to the "migrateTo" address set by the project team.
  • The migrate() function subsequently calls the onMigration() function on the Migration contract. This contract was not provided to us within the scope of this audit, so we are unable to provide an assessment in terms of security.
  • Users can cancel their withdrawal requests at any time.
  • Rewards are automatically calculated and transferred on deposits and withdraws, but any user may also choose to harvest their rewards manually at any time.
  • The Claim Assessor address can use the these contracts to withdraw any amount of the asset token from the RiskPool contract at any time.
  • The owner can set the ExchangeAgent and CapitalAgent contract addresses at any time.
  • The owner can set the reward multiplier to any value at any time.
  • The owner can set the Claim Assessor and migrateTo addresses to any address at any time.
  • The owner can set the minimum LP capital value for the RiskPool to any value at any time.
  • The owner can set the wait time between withdrawals to any duration at any time.
  • The owner can set the staking start time in the SSIP to any value at any time.
  • The owner can use the SyntheticSSIPFactory to create a new SyntheticSSIP contract for the pool at any time.
  • The owner can use the RewarderFactory to create a new Rewarder contract for the pool at any time.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of ControlN/APASS
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Deprecated OpcodesN/APASS
Ether ThiefN/APASS
ExceptionsN/APASS
External CallsN/APASS
Integer Over/UnderflowN/APASS
Multiple SendsN/APASS
SuicideN/APASS
State Change External CallsN/APASS
Unchecked RetvalN/APASS
User Supplied AssertionN/APASS
Critical Solidity CompilerN/APASS
Overall Contract Safety PASS

Contract Source Summary and Visualizations