rStable - Smart Contract Audit Report

Summary

 rStable Audit Report Rena Finance is building rStable, a new token that deposits users' funds into a Yearn Vault to earn rewards that are distributed among stakers.

We reviewed Rena Finance's rStable, rFund, StrategyYearn, rStaking, StakingERC20, Distribute, and UniV2Trader contracts at commit 57f2d84bb1862f2d77517dc1605090549086ca5f on the team's private Github repository.

Notes on Individual Contracts:
rStable Contract:
  • Anyone can use the rStable contract to mint or burn rStable tokens, as long as the address of the rFund contract is set in the contract; the rFund contract address can only be set one time on initialization.
  • The user must pay 0.000000000001 USDC tokens and 0.0000000000000001 RENA tokens in order to mint 1 rStable token and will receive the same amount in return upon burning the token.
  • Upon minting rStable tokens, the USDC tokens are transferred to the rFund contract and immediately invested via the Strategy contract; the Strategy contract deposits the funds into a Yearn Vault to gain interest.
  • The RENA tokens are stored in the rStable contract, and are able to be withdrawn by the owner at any time.
  • Upon burning rStable tokens, the USDC and RENA tokens are returned to the user, provided the contract is able to withdraw the USDC from the Yearn Vault, and there is enough RENA in the contract balance to cover the transaction.
rFund Contract:
  • The rFund contract is used to provide a gateway from the rStable contract to the Strategy contract to support deposits and withdrawals.
  • Anyone can use the rFund contract to harvest rewards in USDC from the Yearn Vault. 90% of the reward is swapped for RENA tokens and sent to the rStaking contract to be distributed among stakers. The remaining 10% is transferred to the project team in USDC.
  • The owner can set the Strategy contract address to any address at any time. The remaining rewards are harvested, and any funds deposited in the current Strategy contract are withdrawn and deposited into the new Strategy contract.
  • The owner can set the trader contract address to any address at any time; as the trader address is responsible for completing all swaps between tokens while harvesting rewards, this contract can be swapped for a malicious contract that collects all the rewards without distributing them.
StrategyYearn Contract:
  • The StrategyYearn contract is called by the rFund contract in order to deposit, withdraw, and harvest rewards from the Yearn Vault.
  • The contract is only able to deposit its own USDC balance into the Yearn Vault. In the event that there is stray USDC in the contract, anyone is able to call the invest() function to deposit it in the Yearn Vault.
rStaking Contract:
  • Anyone is able to use the rStaking contract to deposit the designated stake token and collect rewards.
  • Stakers can collect rewards at any time. Rewards are based on the amount staked and the change in bond value since the user last collected rewards.
  • Stakers can only unstake 24 hours after their latest stake. Rewards are collected upon unstaking.
  • The rFund uses this contract to distribute rewards harvested from the Yearn Vault to stakers.
  • The owner is able to toggle an 'Emergency' state, which prevents users from staking, unstaking, and collecting rewards. However, users are able to use the emergencyUnstake() function, which unstakes the user's funds without claiming rewards.
UniV2Trader Contract:
  • This contract is used by the rFund contract when swapping USDC for RENA tokens in order to distribute among the participants in the rStaking platform. As these functions do not take slippage into account when swapping into RENA tokens, the team must exercise caution as to not swap large amounts at once on a predictable cadence, or these transactions are vulnerable to front-running.
  • The UniV2Trader contract allows anyone to add Uniswap liquidity to the USDC-RENA pair.
  • This contract also allows users to use Uniswap to swap USDC for RENA tokens.

Audit Findings Summary:
  • No security issues from outside attackers were identified.
  • Ensure trust in the team as they have notable control in the ecosystem.
  • Date: October 15th, 2021.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/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

rStable Contract

Smart Contract Graph

Contract Inheritance


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Int] IERC20Metadata (IERC20)
    - [Ext] name
    - [Ext] symbol
    - [Ext] decimals

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  ERC20 (Context, IERC20, IERC20Metadata)
    - [Pub]  #
    - [Pub] name
    - [Pub] symbol
    - [Pub] decimals
    - [Pub] totalSupply
    - [Pub] balanceOf
    - [Pub] transfer #
    - [Pub] allowance
    - [Pub] approve #
    - [Pub] transferFrom #
    - [Pub] increaseAllowance #
    - [Pub] decreaseAllowance #
    - [Int] _transfer #
    - [Int] _mint #
    - [Int] _burn #
    - [Int] _approve #
    - [Int] _beforeTokenTransfer #
    - [Int] _afterTokenTransfer #

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Prv] _setOwner #

 + [Lib] SafeMath 
    - [Int] tryAdd
    - [Int] trySub
    - [Int] tryMul
    - [Int] tryDiv
    - [Int] tryMod
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] div
    - [Int] mod
    - [Int] sub
    - [Int] div
    - [Int] mod

 + [Int] IRFund 
    - [Ext] invest #
    - [Ext] withdraw #

 +  rStable (ERC20, Ownable)
    - [Pub]  #
       - modifiers: ERC20,Ownable
    - [Ext] initialize #
       - modifiers: onlyOwner
    - [Pub] decimals
    - [Pub] mint #
       - modifiers: _initialized
    - [Ext] adminWithdraw #
       - modifiers: onlyOwner
    - [Pub] burn #
       - modifiers: _initialized

rFund Contract

Smart Contract Graph

Contract Inheritance


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] ITrader 
    - [Ext] swapUSDCForRENA #

 + [Int] IRFund 
    - [Ext] invest #
    - [Ext] withdraw #

 + [Int] IStakingERC20 
    - [Ext] distribute #

 + [Int] IStrategy 
    - [Ext] invest #
    - [Ext] withdrawAll #
    - [Ext] withdraw #
    - [Ext] withdrawReward #
    - [Ext] computeReward #

 + [Int] IYearnVault 
    - [Ext] deposit #
    - [Ext] withdraw #
    - [Ext] withdraw #
    - [Ext] balanceOf
    - [Ext] pricePerShare

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Prv] _setOwner #

 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Lib] SafeMath 
    - [Int] tryAdd
    - [Int] trySub
    - [Int] tryMul
    - [Int] tryDiv
    - [Int] tryMod
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] div
    - [Int] mod
    - [Int] sub
    - [Int] div
    - [Int] mod

 +  rFund (IRFund, Ownable)
    - [Pub]  #
       - modifiers: Ownable
    - [Ext]  ($)
    - [Ext] setTrader #
       - modifiers: onlyOwner
    - [Ext] setStrategy #
       - modifiers: onlyOwner
    - [Ext] invest #
       - modifiers: onlyStable
    - [Int] _migrateStrategy #
       - modifiers: onlyOwner
    - [Ext] withdraw #
       - modifiers: onlyStable
    - [Pub] update #
    - [Int] _distribute #

StrategyYearn Contract

Smart Contract Graph

Contract Inheritance


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Lib] SafeMath 
    - [Int] tryAdd
    - [Int] trySub
    - [Int] tryMul
    - [Int] tryDiv
    - [Int] tryMod
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] div
    - [Int] mod
    - [Int] sub
    - [Int] div
    - [Int] mod

 + [Int] IStrategy 
    - [Ext] invest #
    - [Ext] withdrawAll #
    - [Ext] withdraw #
    - [Ext] withdrawReward #
    - [Ext] computeReward #

 + [Int] IYearnVault 
    - [Ext] deposit #
    - [Ext] withdraw #
    - [Ext] withdraw #
    - [Ext] balanceOf
    - [Ext] pricePerShare

 +  StrategyYearn (IStrategy)
    - [Pub]  #
    - [Ext] invest #
    - [Pub] invest #
    - [Ext] getBalance
    - [Ext] withdrawAll #
       - modifiers: onlyRFund
    - [Pub] withdraw #
       - modifiers: onlyRFund
    - [Pub] computeReward
    - [Ext] withdrawReward #
       - modifiers: onlyRFund
    - [Pub] getStoredUSDC

rStaking Contract

Smart Contract Graph

Contract Inheritance


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Lib] Address 
    - [Int] isContract
    - [Int] sendValue #
    - [Int] functionCall #
    - [Int] functionCall #
    - [Int] functionCallWithValue #
    - [Int] functionCallWithValue #
    - [Int] functionStaticCall
    - [Int] functionStaticCall
    - [Int] functionDelegateCall #
    - [Int] functionDelegateCall #
    - [Prv] _verifyCallResult

 + [Lib] SafeERC20 
    - [Int] safeTransfer #
    - [Int] safeTransferFrom #
    - [Int] safeApprove #
    - [Int] safeIncreaseAllowance #
    - [Int] safeDecreaseAllowance #
    - [Prv] _callOptionalReturn #

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Prv] _setOwner #

 + [Lib] SafeMath 
    - [Int] tryAdd
    - [Int] trySub
    - [Int] tryMul
    - [Int] tryDiv
    - [Int] tryMod
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] div
    - [Int] mod
    - [Int] sub
    - [Int] div
    - [Int] mod

 +  Distribute (Ownable)
    - [Pub]  #
       - modifiers: Ownable
    - [Pub] stakeFor #
       - modifiers: onlyOwner
    - [Pub] unstakeFrom #
       - modifiers: onlyOwner
    - [Pub] unstakeNoRewardsFrom #
       - modifiers: onlyOwner
    - [Ext] withdrawFrom #
       - modifiers: onlyOwner
    - [Ext] distribute ($)
       - modifiers: onlyOwner
    - [Ext] totalStakedFor
    - [Ext] totalStaked
    - [Pub] getReward
    - [Ext] getTotalReward
    - [Int] _getReward

 + [Int] IERC900 
    - [Ext] stake #
    - [Ext] stakeFor #
    - [Ext] unstake #
    - [Ext] totalStakedFor
    - [Ext] totalStaked
    - [Ext] token
    - [Ext] supportsHistory

 +  StakingERC20 (Ownable, IERC900)
    - [Pub]  #
       - modifiers: Ownable
    - [Ext] activateEmergency #
       - modifiers: onlyOwner
    - [Ext] distribute #
    - [Ext] stake #
    - [Pub] stakeFor #
    - [Pub] unstake #
    - [Ext] emergencyUnstake #
    - [Ext] withdraw #
    - [Pub] totalStakedFor
    - [Ext] totalStaked
    - [Ext] totalReward
    - [Ext] token
    - [Ext] supportsHistory
    - [Pub] getReward

 +  rStaking (StakingERC20)
    - [Pub]  #
       - modifiers: StakingERC20
    - [Pub] stakeFor #
    - [Pub] unstake #

UniV2Trader Contract

Smart Contract Graph

Contract Inheritance


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public
 
 + [Int] IERC20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Int] IUniswapV2Router01 
    - [Ext] factory
    - [Ext] WETH
    - [Ext] addLiquidity #
    - [Ext] addLiquidityETH ($)
    - [Ext] removeLiquidity #
    - [Ext] removeLiquidityETH #
    - [Ext] removeLiquidityWithPermit #
    - [Ext] removeLiquidityETHWithPermit #
    - [Ext] swapExactTokensForTokens #
    - [Ext] swapTokensForExactTokens #
    - [Ext] swapExactETHForTokens ($)
    - [Ext] swapTokensForExactETH #
    - [Ext] swapExactTokensForETH #
    - [Ext] swapETHForExactTokens ($)
    - [Ext] quote
    - [Ext] getAmountOut
    - [Ext] getAmountIn
    - [Ext] getAmountsOut
    - [Ext] getAmountsIn

 + [Int] IUniswapV2Router02 (IUniswapV2Router01)
    - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
    - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
    - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
    - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
    - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #

 + [Int] ITrader 
    - [Ext] swapUSDCForRENA #

 +  UniV2Trader (ITrader)
    - [Pub]  #
    - [Ext] _addLiquidityUsdcRena #
    - [Ext] swapUSDCForRENA #