Phiat Protocol

Smart Contract Audit Report

Audit Summary

Phiat Protocol Audit Report PHIAT is releasing a new decentralized lending platform where users can borrow assets after depositing collateral. Users can additionally earn rewards by staking $PHIAT tokens.

For this audit, we reviewed the following contracts:

Audit Findings

All findings have been resolved, though some centralized aspects are present.
Date: March 2nd, 2023.
Updated: March 28th, 2023 with changes from commit a5628de5730e88bd7d17cb9188ad864ad8f243e1 to commit c4559553bf9430814282324ce349f170e7976977.
Updated: April 13th, 2023 with mainnet addresses.

Finding #1 - UniswapV3PhiatPriceAggregator - Low (Acknowledged)

Description: The Oracle uses a 5 minute time period to calculate the average price.
Risk/Impact: As validators in a POS system are able to see what blocks they will be attesting to in advance, they can anticipate when they will be attesting to two blocks in a row. A validator staking a sufficient amount can then maintain a manipulated price across two blocks and thus enter it into the Uniswap Pair's price history.
Recommendation: The cost of this type of manipulation increases with the Oracle's time period. The team should consider increasing the Oracle's time period to 30 minutes to make this type of manipulation largely impractical.
Resolution: The team will only be using a UniswapV3PhiatPriceAggregator contract for tokens that do not have a Chainlink aggregator and are thus more likely to be a volatile asset. As such, they have elected to maintain a smaller Oracle window to more accurately track assets that will maintain potentially large price swings in a short period of time. They intend to mitigate any potential risk by using a low loan-to-value ratio and borrow limits for such assets.

Finding #2 - PhiatBackupPriceOracle - Low (Resolved)

Description: The Oracle returns a manually set price if the asset has no UniswapV3PhiatPriceAggregator set or it fails and returns 0.
Risk/Impact: An improperly set value can be returned allowing a malicious user to exploit this price within the platform.
Recommendation: The team must ensure to set a UniswapV3PhiatPriceAggregator for all assets as well as setting a backup value. The backup values should be routinely maintained to prevent a large disparity between the true price and the backup value. Additionally, the team should consider reverting in the AaveOracle if the PhiatBackupPriceOracle returns a value of 0.
Resolution: The team has modified the AaveOracle contract so that it will revert if the PhiatBackupPriceOracle returns a value of 0 for a token's current price.

Finding #3 - LendingPool - Low (Resolved)

Description: Users can be blacklisted through the repayAllAndBlacklist() function if their health factor reaches 0. However, they are still able to call the borrow() function.
Risk/Impact: If a user is blacklisted but their collateral later regains value they will be able to borrow again.
Recommendation: The team should consider adding the _isNotBlacklisted modifier to the borrow() function.
Resolution: By preventing an AaveOracle return value of 0 the team has prevented any situation in which a user would be able to borrow again after they are blacklisted.

Finding #4 - MultiOwnable - Informational (Resolved)

Description: This contract is never used within the platform.
Recommendation: The team should remove this contract if it will remain unused.
Resolution: The team has removed the contract from the repository.

Finding #5 - PhiatTimelockController - Informational (Resolved)

Description: The updateReserveLimits() function divides before multiplying resulting in a loss of precision.
Recommendation: The team should multiply the liquidity value before dividing.
Resolution: The team has modified the updateReserveLimits() function so the multiplication is performed before the division.

Contracts Overview

AToken Contract:
  • aTokens are interest bearing tokens that correspond to an underlying asset; an aWETH aToken would correspond to an underlying asset of WETH.
  • aTokens are minted to the user when they deposit the underlying asset into the Lending Pool.
  • aTokens are burned and the corresponding asset is returned to the user when withdrawing from the Lending Pool.
  • aTokens can only be transferred if the sending user's health factor does not drop below the current liquidation threshold.
  • The contract has the ability to mint rewards in an IncentivesController contract when tokens are minted or burned, though the team has elected not to implement one.
  • The contract implements the EIP-2612 standard to support "permit" functionality which allows for approvals to be made via signatures.
  • The contract complies with the ERC-20 standard.
  • Other than the ERC-20 and EIP-2612 functions, all functions that affect the state of the token can only be called by the Lending Pool.
StableDebtToken Contract:
  • StableDebtTokens are minted to a user when they borrow from the Lending Pool and burned from the user when they repay their position.
  • As the name suggests, StableDebtTokens represent a borrowing position that has a stable interest rate.
  • The contract has the ability to mint rewards in an IncentivesController contract when tokens are minted or burned, though the team has elected not to implement one.
  • These tokens are modeled after the ERC20 standard, but do not fully implement the standard. As debt is non-transferable, there are no transfer functions for StableDebtTokens.
  • Similar to the aToken, all non-ERC20 public functions that affect state can only be called by the Lending Pool.
VariableDebtToken Contract:
  • VariableDebtTokens are minted to a user when they borrow from the Lending Pool and burned from the user when they repay their position.
  • As the name suggests, VariableDebtTokens represent a borrowing position that has a variable interest rate.
  • The variable interest rate is constantly changing based on the utilization rate. The utilization rate is a measure of the liquidity of the corresponding asset existing in the Lending Pool.
  • The contract has the ability to mint rewards in an IncentivesController contract when tokens are minted or burned, though the team has elected not to implement one.
  • These tokens are modeled after the ERC20 standard, but do not fully implement the standard.
  • As debt is non-transferable, there are no transfer functions for VariableDebtTokens.
  • Similar to the aToken, all non-ERC20 public functions that affect state can only be called by the Lending Pool.
LendingPool Contract:
  • This contract allows non-blacklisted users to deposit various supported tokens into the Lending Pool.
  • aTokens corresponding to the deposited asset are minted to the user. When withdrawing, these aTokens are burned.
  • Users can also deposit and withdraw on behalf of another non-blacklisted address, if desired.
  • A users total deposited amount must be greater than the minimum individual deposit size and less than the maximum deposit size for the token, if they have been set.
  • The total deposits across all users for the token may not exceed the maximum global deposit size, if it has been set.
  • A users deposited amount must remain above the minimum individual deposit size when withdrawing less than their total deposited tokens.
  • A user can borrow an amount of an asset from its reserve, provided that the user has deposited enough funds to be used as collateral.
  • Users must manually set their funds to be used as collateral before borrowing. The collateralized loans are then locked and cannot be withdrawn.
  • A users total borrowed amount must be less than the maximum borrow size for the token, if it has been set.
  • The total borrowed tokens across all users for the token may not exceed the maximum global borrow size, if it has been set.
  • The percentage of borrowed assets across all users may not exceed the maximum borrow basis points for the token, if it has been set.
  • When borrowing, users choose either a stable or variable interest rate model, where corresponding StableDebtTokens or VariableDebtTokens will be minted to the borrower to track their debt.
  • Users can swap between interest rate models at any time.
  • Borrowed funds have no deadline to be paid back, however interest will be accrued as time passes. This can lead to a decrease in the loan's health factor, based on the liquidation threshold and the value of the borrowed assets relative to the value of the users deposited collateral.
  • A reserve factor of all accrued interest is minted to the Treasury address.
  • If the health factor drops below 1 and above 0.95, another user can liquidate up to 50% of the funds borrowed and receive a portion of the collateral plus an additional percentage of the collateral as a bonus.
  • If the health factor drops below 0.95, another user can liquidate up to 100% of the funds borrowed and receive all of the users deposited collateral.
  • The user performing the liquidation can select to receive the collateral aTokens or the actual underlying asset back.
  • A user's health factor can be increased by either repaying borrowed funds or by depositing further collateral.
  • Users can specify a different address to receive debt, provided they have been given a credit delegation allowance. StableDebtTokens/VariableDebtTokens are then minted and burned to or from this different address.
  • If a user's health factor reaches zero, any address may repay all of their outstanding debt. This will then blacklist the borrowing user.
  • This contract also implements flashloan functionality, where anyone can borrow all of the assets in any liquidity pool, as long as they are returned within the transaction. A premium of 0.09% of each pool used in the flash loan is charged as a fee.
  • When depositing, borrowing, or taking out a flashloan, users are able to specify a referral address.
  • A Price Oracle contract is used to calculate price data, and can be changed at any time by the Pool Admin of the LendingPoolAddressProvider address.
  • The Lending Pool Configurator of the LendingPoolAddressesProvider contract has the ability to add new assets at any time.
  • The owner of the LendingPoolAddressProvider contract has the ability to update the LendingPool contract implementation at any time.
  • The owner of the LendingPoolAddressProvider contract can update this contract's LendingPoolCollateralManager address at any time.
LendingPoolConfigurator Contract:
  • A Pool Admin, defined in the LendingPoolAddressesProvider contract, has power to make various configuration updates to the lending pools through this contract.
  • An Emergency Admin, also defined in the LendingPoolAddressesProvider contract, has the ability to pause all transactions through this contract.
  • The Pool Admin and Emergency Admin roles can be changed at any time by the owner of the LendingPoolAddressesProvider contract.
  • A Pool Admin has the ability to make any of the following changes at any time:
    • Update the implementation of any aToken.
    • Update the implementation of the StableDebtToken or VariableDebtToken.
    • Allow or disallow any kind of borrowing on any reserve.
    • Configure the loan to value ratio of an asset used as collateral.
    • Update the bonus percentage earned when liquidating an asset.
    • Update an asset's health factor threshold to enable liquidations.
    • Activate or deactivate any reserve.
    • Freeze or unfreeze any reserve, disallowing/allowing deposits, borrows, and rate swaps.
    • Change a reserve's strategy contract used to generate interest.
    • Update the reserve factor of an asset, which is the factor of how much of an asset is stored in a separate contract as reserve.
    • Update the minimum and maximum individual deposit amounts for a token.
    • Update the maximum global deposit amount for a token.
    • Update the maximum individual borrow amount and global borrow amount for a token.
    • Update the maximum global borrow basis points for a token.
AaveOracle Contract:
  • This contract is used to track the price of various assets determined by Sources.
  • The current asset price is determined by the price reported by the asset's Source.
  • If the asset has no Source or the Source returns a price of 0, the Fallback Oracle is used to determine price.
  • The Owner may set the Source for any asset at any time.
  • The owner may update the Fallback Oracle address at any time.
PhiatBackupPriceOracle Contract:
  • This contract is used to track the price of various assets determined by Sources.
  • If the "aggregator weight" is 0 and the asset has a manually set price, the current asset price returned will be the manually set price.
  • If the aggregator weight is not 0 and the asset has no source, the current asset price returned will be the manually set price.
  • If the aggregator weight is 100%, the current asset price returned will be the price returned by the Source.
  • In all other cases, the current asset price returned will be the weighted average of the manually set price and the Source price.
  • The Source price will be weighted proportionally to the aggregator weight value.
  • The owner may set the Source for any asset at any time.
  • The owner may set the aggregator weight at any time.
  • The owner may add and remove any address as a Keeper at any time.
  • Keepers may set the manual price for any asset at any time.
UniswapV3PhiatPriceAggregator
  • This contract serves as a Time-Weighted Average Price (TWAP) Oracle for a specified token.
  • The contract uses Uniswap V3 Tick values to determine the average price over the most recent 5 minute period.
LendingRateOracle Contract:
  • This contract is used to track the borrow rate and liquidity rate for various assets.
  • The owner may set the borrow rate for any asset at any time.
  • The owner may set the liquidity rate for any asset at any time.
PlsUsdPriceOracle Contract:
  • This contract is used to track the price of various assets determined by Sources.
  • If the "aggregator weight" is 0 and the asset has a manually set price, the current asset price returned will be the manually set price.
  • If the aggregator weight is not 0 and the asset has no source, the current asset price returned will be the manually set price.
  • If the aggregator weight is 100%, the current asset price returned will be the price returned by the Source.
  • In all other cases, the current asset price returned will be the weighted average of the manually set price and the Source price.
  • The Source price will be weighted proportionally to the aggregator weight value.
  • The owner may set the Source for any asset at any time.
  • The owner may set the aggregator weight at any time.
  • The owner may add and remove any address as a Keeper at any time.
  • Keepers may set the manual price for any asset at any time.
  • This contract is intended for frontend use only.
PhiatToken Contract:
  • This contract defines the "Pulse & Hex Investor Allocation Tools" (PHIAT) token.
  • The total max supply of $PHIAT tokens is 55 million [55,555,000].
  • PHIAT tokens may not be transferred before the "mint start time".
  • No tokens are minted upon deployment, but the total supply of tokens is allocated to the Treasury address.
  • Any address may mint their allocated tokens after the mint start time and before the "mint expiration time".
  • The owner may set the allocated tokens for any address before the "mint lock time".
  • The Treasury address may mint an address' allocated tokens after the mint expiration time.
  • The Treasury address may update the Treasury address at any time. They will receive any remaining mints from the original Treasury.
PhiatFeeDistribution Contract:
  • This contract is used to reward stakers in multiple types of reward tokens.
  • All aTokens are added as rewards during their Reserve initialization.
  • Users will earn rewards as rewards tokens are transferred to the contract.
  • Rewards will not begin accumulating until the getRewards() function is called after the reward token is initially added.
  • Each time the getRewards() function is called all new tokens transferred to the contract will be distributed to token holders over a "rewards period".
  • Tokens are distributed based on the amount of time staked from the last time rewards are claimed and the current reward rate.
  • Rewards are accumulated when users deposit, unstake, and withdraw but must be manually collected.
  • The Treasury address will accumulate rewards for the total supply of the staking token that is not currently staked in this contract.
  • Any address may stake a specified amount of the staking token at any time.
  • Users may unstake their staked tokens at any time, but may not withdraw them until the "unstake duration" has passed.
  • Unstaked tokens will continue to accumulate rewards until they are withdrawn.
  • Users may cancel an unstake until the unstake duration has passed.
  • Users may withdraw their unstaked tokens after the unstake duration has passed and before the "withdraw duration" passes.
  • Once the withdraw duration has passed users will need to unstake their tokens and wait the unstake duration again.
  • Users may claim their accumulated rewards at any time.
  • The owner may add a new reward token at any time.
  • The Treasury address may withdraw any non-staking and non-reward tokens at any time.
  • The Treasury address may update its own address at any time.
  • The team must exercise caution that the staking token is not a fee-on-transfer token.
PhiatMultisigTreasury Contract:
  • This contract is used to allow owners to queue Transactions that execute arbitrary logic and to manage the Phiat token and PhiatFeeDistribution contracts.
  • A list of owners is specified upon deployment.
  • Any owner may mint the Phiat tokens allocated to the Treasury at any time.
  • Any owner may mint Phiat tokens allocated to users after the expiration time.
  • Any owner may claim rewards from the PhiatFeeDistribution address at any time.
  • Any owner may submit a new Transaction. They will automatically be added to the confirmations for the Transaction.
  • Any owner may add their confirmation for a submitted Transaction.
  • Any owner may remove their confirmation for a Transaction they have previously confirmed.
  • Once the required number of confirmations have been reached any owner who confirmed the Transaction may execute it.
  • The contract will then perform the logic included in the Transaction on the destination address.
  • This contract may add, remove, and replace any owner through a Transaction.
  • This contract may update the required number of confirmations through a Transaction.
  • This contract may update the PhiatFeeDistribution address through a Transaction.
PhiatMultisigGov Contract:
  • This contract is used to allow owners to queue Transactions that execute arbitrary logic and to manage the Timelock address.
  • A list of owners is specified upon deployment.
  • Any owner may update the reserve limits through the Timelock address. They may optionally specify a reserve to update a single reserve's reserve limits.
  • Any owner may submit a new Transaction. They will automatically be added to the confirmations for the Transaction.
  • Any owner may add their confirmation for a submitted Transaction.
  • Any owner may remove their confirmation for a Transaction they have previously confirmed.
  • Once the required number of confirmations have been reached any owner who confirmed the Transaction may execute it.
  • The contract will then perform the logic included in the Transaction on the destination address.
  • This contract may add, remove, and replace any owner through a Transaction.
  • This contract may update the required number of confirmations through a Transaction.
PhiatTimelockController Contract:
  • This contract is used to allow an Admin to queue Transactions that execute arbitrary logic after a delay.
  • The Admin address may schedule a Transaction at any time.
  • The delay for a Transaction must be at least the "minimum delay".
  • A Transaction may optionally contain several external calls.
  • The Admin may cancel a scheduled Transaction before its delay has passed.
  • Once the Transaction's delay has passed and the previous Transaction has been executed or cancelled, the Admin may execute a scheduled Transaction.
  • The Admin address may update the reserve limits for a single reserve or all reserves. The reserve limits are determined based on the current liquidity returned by the Liquidity Oracle address.
  • The reserve limits may only be decreased.
  • The max global deposit size will be set to 10% of the current liquidity value. The max individual deposit size will be set to 1% of the current liquidity value.
  • The max global borrow size will be set to 6% of the current liquidity value. The max individual borrow size will be set to 0.6% of the current liquidity value.
  • This contract may update the Admin, Addresses Provider, and Liquidity Oracle addresses through a Transaction.
PhiatMarketLiquidityOracle Contract:
  • This contract is used to track the liquidity of various assets determined by Sources.
  • The current asset liquidity is the latest value supplied by the asset's Source.
  • If the asset has no Source, a liquidity of 0 will be returned.
  • The owner may set the Source for any asset at any time.
UniswapPhiatLiquidityAggregator Contract:
  • This contract is used to track liquidity across multiple pools.
  • The latest value when queried will be the current sum of the token balance across all of the pools.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • The LendingPoolConfigurator Pool Admin can add any asset to the LendingPool.
  • The LendingPoolConfigurator Pool Admin can freeze any asset in the LendingPool.
  • The AaveOracle owner can set the source for any asset.
  • The PhiatBackupPriceOracle owner can set the source for any asset.
  • The PhiatBackupPriceOracle Keepers may set the manual price for an asset.
  • The PhiatMultisigGov can update any asset's reserve limits through the PhiatTimelockController.
WARNING
Compiler Issues N/A PASS
Delegate Call to Untrusted Contract N/A PASS
Dependence on Predictable Variables N/A PASS
Ether/Token Theft N/A PASS
Flash Loans N/A PASS
Front Running N/A PASS
Improper Events N/A PASS
Improper Authorization Scheme N/A PASS
Integer Over/Underflow N/A PASS
Logical Issues N/A PASS
Oracle Issues N/A PASS
Outdated Compiler Version N/A PASS
Race Conditions N/A PASS
Reentrancy N/A PASS
Signature Issues N/A PASS
Sybil Attack N/A PASS
Unbounded Loops N/A PASS
Unused Code N/A PASS
Overall Contract Safety   PASS

Contract Source Summary and Visualizations

Name

Address/Source Code

Visualized
(Hover-Zoom Recommended)

PhiatMultisigGov

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatTimelockController

ETH Mainnet

Inheritance Chart.  Function Graph.

LendingPool

ETH Mainnet

Inheritance Chart.  Function Graph.

LendingPoolConfigurator

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatFeeDistribution

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatMultisigTreasury

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatToken

ETH Mainnet

Inheritance Chart.  Function Graph.

AToken

ETH Mainnet

Inheritance Chart.  Function Graph.

StableDebtToken

ETH Mainnet

Inheritance Chart.  Function Graph.

VariableDebtToken

ETH Mainnet

Inheritance Chart.  Function Graph.

AaveOracle

ETH Mainnet

Inheritance Chart.  Function Graph.

LendingRateOracle

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatBackupPriceOracle

ETH Mainnet

Inheritance Chart.  Function Graph.

PhiatMarketLiquidityOracle

ETH Mainnet

Inheritance Chart.  Function Graph.

PlsUsdPriceOracle

ETH Mainnet

Inheritance Chart.  Function Graph.

UniswapPhiatLiquidityAggregator

GitHub

Inheritance Chart.  Function Graph.

UniswapV3PhiatPriceAggregator

GitHub

Inheritance Chart.  Function Graph.

About SourceHat

SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

Contact us today to get a free quote for a smart contract audit of your project!

What is a SourceHat Audit?

Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

How Do I Interpret the Findings?

Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

  • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
  • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
  • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
  • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.