Unreal Finance

CertiKFiles named in the report

CertiK reviewed Unreal Finance on 13 Oct 2022: 20 findings, 0 critical; 16 marked fixed, 4 open.

No open critical findings
Open the reportOpen Unreal Finance’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings20

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High201
Medium001
Low2014
HighUnprotected Upgradeable ContractFixed

`Core` is an upgradeable contract that does not protect its `initialize()` function. Anyone can delete the contract with: `UUPSUpgradeable.upgradeTo(address)` or `UUPSUpgradeable.upgradeToAndCall(address, bytes)`. ```solidity function initialize(address _treasuryAddress) public initializer{ ``` ```solidity function upgradeTo(address newImplementation) external virtual onlyProxy { ``` ```solidity function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { ```

MediumShadowing State VariableFixed

A state variable is shadowing another component defined in a parent contract. Variable `_owner` in `Core` shadows the variable `_owner` in `OwnableUpgradeable`. ```solidity=18 address private _owner; ``` ```solidity=22 address private _owner; ```

Low`_decimals` can be made privateFixed

Variable `_decimals` can be made private to avoid two getter functions `_decimals()` and `decimals()` that return the same value.

Low`_protocol` may be different from what `_bytecodeFixed

The input `_protocol` used to check whether the given protocol is supported does not necessarily have to match the protocol that corresponds to the futures contract implemented through the input `_bytecode`. It is possible bytecode for a futures contract corresponding to one protocol is stored under the `streamKey` of a different protocol.

Low`amountUnderlying` may be larger than `totalSupplFixed

`_amountUnderlying` is the amount of underlying asset the `msg.sender` wishes to deposit, while `totalSupply` refers to the total supply of associated yield tokens for that underlying asset. The yield tokens are in one-to-one ratio with the asset tokens that are deposited, and they have the same decimals as the underlying asset. It is possible for the entire product `yield * _amountUnderlying / totalSupply` to exceed the value of `_amountUnderlying`, causing a revert due to underflow in `amountOT`. This may keep a user from depositing the underlying asset.

LowIncompatibility with Deflationary TokensFixed

When transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived to the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction. Reference: https://thoreum-finance.medium.com/what-exploit-happened-today-for-gocerberus-and-garuda-also-for-lokum-ybear-piggy-caramelswap-3943ee23a39f ```solidity=31 IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount); ``` - Transferring tokens by `_amount`. ```solidity=33 ILendingPool(getProtocolFrontend()).deposit(underlying, _amount, address(this), 0); ``` - The `_amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees. - Note: `deposit` is an external function and its behavior wasn't evaluated. --- ```solidity=31 IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount); ``` - Transferring tokens by `_amount`. ```solidity=33 IPool(getProtocolFrontend()).supply(underlying, _amount, address(this), 0); ``` - The `_amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees. - Note: `supply` is an external function and its behavior wasn't evaluated. --- ```solidity=30 IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount); ``` - Transferring tokens by `_amount`. ```solidity=32 cToken.mint(_amount); ``` - The `_amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees. - Note: `mint` is an external function and its behavior wasn't evaluated. --- ```solidity=120 deposit(_amountInUnderlying); ``` - Transferring tokens by `_amountInUnderlying`. - This function call executes the following operation. - In `AFuture.deposit`, - `IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount);` ```solidity=120 deposit(_amountInUnderlying); ``` - This function call executes the following operation. - In `AFuture.deposit`, - `ILendingPool(getProtocolFrontend()).deposit(underlying, _amount, address(this), 0);` - Note: `deposit` is an external function and its behavior wasn't evaluated. - The `_amountInUnderlying` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=120 deposit(_amountInUnderlying); ``` - Transferring tokens by `_amountInUnderlying`. - This function call executes the following operation. - In `YFuture.deposit`, - `IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount);` ```solidity=120 deposit(_amountInUnderlying); ``` - This function call executes the following operation. - In `YFuture.deposit`, - `yVault.deposit(_amount, address(this));` - Note: `deposit` is an external function and its behavior wasn't evaluated. - The `_amountInUnderlying` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=126 deposit(_amount); ``` - Transferring tokens by `_amount`. - This function call executes the following operation. - In `AFuture.deposit`, - `IERC20(underlying).safeTransferFrom(msg.sender, address(this), _amount);` ```solidity=126 deposit(_amount); ``` - This function call executes the following operation. - In `AFuture.deposit`, - `ILendingPool(getProtocolFrontend()).deposit(underlying, _amount, address(this), 0);` - Note: `deposit` is an external function and its behavior wasn't evaluated. - The `_amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees.

LowMissing Emit EventsFixed

Functions that affect the status of sensitive variables should emit events as notifications.

LowMissing Input ValidationFixed

- Input `_underlying` is missing a check that it is a non-zero address - Input `_durationSeconds` is missing a check that it is a non-zero value. - Input `_amountUnderlying` is missing a check that it is a non-zero value. - Input `_supply` is missing a check that it is a non-zero value. - Input `_core` and `_treasuryAddr` are missing a check that they are non-zero addresses. - State variable `yT` is missing a check that it is a non-zero address. - State variable `oT` is missing a check that it is a non-zero address. - Local variables `lendingProvider`, `compToken`, and `yearnVault` are missing a check that they are non-zero addresses.

LowNo check `amountBurned` is positive before `claimFixed

Function `redeemYield()` can be called more than once, where all subsequent times will be for an `amountBurned` value of 0. Like function `redeemPrinciple()`, there should be a check that `amountBurned` is positive before proceeding with `claimYield()`.

LowNo validation check that `streamKey` is not `byteFixed

In function `startEpoch()`, a check is made that the calculated `streamKey` corresponding to the input for `_protocol`, `_underlying`, and `_durationSeconds` is not `bytes32(0)`. No such check is made in `registerNewStream()`.

Show 6 more
LowOwner Inputs `_bytecode` for `create2`Fixed

The construction of a futures contract depends on the correct input for `_bytecode` by the `_owner` of contract `Core`. However, the only check that the intended contract deployment was successful is that the `owner()` of the new futures contract is `address(this)`.

LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaFixed

The return value of the transfer()/transferFrom() call is not checked. ```solidity=43 IERC20(underlying).transfer(treasury, underlyingbalance); ``` --- ```solidity=83 yT.transfer(_receiver, _amount); ``` --- ```solidity=93 oT.transfer(_receiver, _amount); ``` --- ```solidity=40 IERC20(underlying).transfer(treasury, underlyingbalance); ```

LowUnlocked Compiler VersionFixed

The contracts cited have unlocked compiler versions. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to differing compiler version numbers. This can lead to an ambiguity when debugging as compiler specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one.

LowUnused Return ValueFixed

The return value of an external call is not stored in a local or state variable. ```solidity=66 prevEpochInstance.expire(); ``` --- ```solidity=42 ILendingPool(getProtocolFrontend()).withdraw(underlying, _amount, treasury); ``` --- ```solidity=42 IPool(getProtocolFrontend()).withdraw(underlying, _amount, treasury); ``` --- ```solidity=32 cToken.mint(_amount); ``` --- ```solidity=41 cToken.redeem(_amountCToken); ``` --- ```solidity=29 yVault.deposit(_amount, address(this)); ``` --- ```solidity=38 yVault.withdraw(_amount); ```

LowUnused State VariableFixed

One or more state variables are never used in the codebase. Variable `_owner` in `Core` is never used in `Core`. ```solidity=18 address private _owner; ``` ```solidity=15 contract Core is Initializable, UUPSUpgradeable, OwnableUpgradeable { ``` --- Variable `initialized` in `Core` is never used in `Core`. ```solidity=20 bool initialized; ``` ```solidity=15 contract Core is Initializable, UUPSUpgradeable, OwnableUpgradeable { ```

LowVariables Could Be Declared as `immutable`Fixed

The variables `provider`, `yVault`, `cToken`, and `_decimals` assigned in the constructor can declared with `Immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. An advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since will not be stored in storage.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (13)

  • contracts/futures/FutureBase.sol
  • contracts/libs/MathLib.sol
  • contracts/futures/AaveV3Future.sol
  • contracts/tokens/YieldToken.sol
  • contracts/libs/Utils.sol
  • contracts/futures/YFuture.sol
  • contracts/Treasury.sol
  • contracts/tokens/OwnershipToken.sol
  • contracts/Core.sol
  • contracts/futures/AFuture.sol
  • contracts/futures/CFuture.sol
  • contracts/libs/DateTime.sol
  • contracts/libs/DetailedERC20.sol

Part of Unreal Finance’s security record

Unreal Finance
Audits1 audit by 1 firmLast audit Oct 2022, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open Unreal Finance’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us