Revest - Smart Contract Audit Report
Revest is a new and innovative protocol which enables users to store value in the form of locked ERC20 tokens as ERC-1155 NFTs. This mechanism enables the use of time and value-locked assets in decentralized financial markets in a standardized manner.We audited Revest at commit 042658fa384d9f1ee406d2ce64e2bb3b281565ad on the project's private GitHub repository.
Audit Findings Summary:
Notes on Individual Contracts
- No security issues from outside attackers were identified.
- The project has implemented a series of recommendations from our team to resolve security issues and reap gas savings.
- Investing requires placing trust in the project team as they have substantial power in the ecosystem and can upgrade critical variables used in the protocol.
- Initial Review Date: September 5th, 2021
- Update Date: September 12th, 2021 - Updates to commit e33f42973263cb3b34f3dccd7346b68a9f05bc8a
- Update Date: September 20th, 2021 - Updates to commit 042658fa384d9f1ee406d2ce64e2bb3b281565ad
- The initial supply of the token will be set by the team upon deployment.
- After deployment, the token cannot be minted; those tokens can be burned.
- There are no fees on transfers of the token.
- The team has the ability to pause and unpause transfers of the token at any time.
- This is the main contract of the protocol, used to mint FNFTs.
- Any user may call mintTimeLock() and deposit ERC20 tokens in order to mint FNFTs which lock the underlying ERC20 token for a provided amount of time.
- In addition, when minting users will also specify how many FNFTs should be created, and how many ERC20 tokens should underly each FNFT.
- The end time of a time lock can be extended by the holder of that FNFT.
- The mintValueLock() functions in a similar manner, but instead of tokens unlocking after a certain amount of time, they only unlock when the price of the underlying asset breaches a specified threshold.
- Pricing data used in determining if a value lock can be unlocked comes from either Chainlink oracles or on-chain time-weighted pricing data; both of which are resistant to flash loan manipulation.
- Finally, users can also mint address locks using mintAddressLock().
- Address locks unlock their underlying value when a specified address makes a call to unlock.
- The purpose of address locks is so arbitrary contracts can be built with custom unlocking logic.
- There is a fee associated with minting, charged in a set amount of ETH or a percentage of the ERC20 token deposited at the time of minting.
- Fees can be updated by the team and are sent to the team's wallet.
- FNTFs can also be further split if their underlying assets are divisible. For example, splitting the value of one original FNFT by producing two new FNFTs with half of the original's value.
- All FNFTs can have additional value contributed to them as well.
- The splitFNFT() function has external calls inside of a loop. Users of this function should limit the number of addresses provided in the "proportions" input variable.FNFTHandler:
- This contract mints the ERC-1155 tokens which will be used to represent future rights to a locked asset.
- Only contract which implement the ERC1155Receiver standard are able to hold the token.
- Only the Revest Controller contract has the ability to mint tokens.
- The Revest Controller contract can also burn the tokens from any address, and update the uri of the token.
- As the contract complies with the ERC-1155 token standard, it will be tradable on any marketplace which supports this standard.
- The _beforeTokenTransfer() function has external calls inside of a loop. When sending tokens in batches, the batch must be limited or split across multiple transactions as to not exceed the block.gaslimit.
- Select users can participate in the private sale by sending ETH directly to the contract.
- Participants must either be whitelisted by the team, or hold a negative entropy NFT.
- Users will be minted an ERC-1155 token to represent their deposit into the private sale contract.
- By default transfers of this ERC-1155 token are disabled, but can be enabled/disabled by the team.
- If the whitelist is enabled by the team and a user who is not whitelisted wishes to participate, they must hold an negativeEntropy NFT which can only be utilized once in the sale.
- The team will determine which addresses are added to the whitelist.
- The team can withdraw the ETH balance of the contract at any time.
- The team can also set the address of the burner contract used to burn the ERC-1155 token upon redemption, and the negative Entropy NFT address.
- The team also has the ability to update the uri of the token, the floor price, the allocation to NFT holders, and the cap.
- The burn(), setBurner(), setFloor(), addToWhitelist(), batchAddToWhitelist(), adjustCap(), withdraw(), getBurner(), getBalance(), getRemainingEth(), getFloor(), getWhitelistEnabled(), getTransferable(), getTicket(), getTicketCap(), getTicketAllocation(), and getNegativeEntropyCount() functions should be declared external instead of public to save some gas on each call.
Public Sale Contract:
- This contract allows users to purchase tokens directly from the project team.
- All tokens will be divided proportionally among all contributors and the price of the token will be determined as ratio of the amount of ETH contributed to the sale.
- Users participate in the sale by sending ETH to the contract once the sale has begun.
- If users invest in the presale before the Early Bird timestamp passes, they can purchase tokens at a reduced price.
- The Early Bird timestamp will be set by the team upon deployment.
- Once the sale has ended, users will be able to claim their tokens.The owner of the contract can initiate a claim on a user's behalf as well.
- The team can withdraw the ETH collected from the sale at any time.
- The team can update the token's address and amount to be sold at any time.
- The team can also manually set addresses to have a claimable balance once the sale concludes.
- This contract will collect the fees from valuts.
- Fees held in the contract will be used as rewards for users based on a user's portion of the total points.
- The erc20Fee variable is unused and can be removed.
- This contract enables users to deposits ERC20 tokens in order to create an FNFT. FNFTs are created via an external call to the Revest contract.
- This contract allows any user to create a lock for assets to be deposited into, in addition with creating an oracle to determine that asset's price.
- Only the RevestController contract has the ability to unlock FNFTs and release their underlying assets.
- This contract contains attributes to access off-chain data for NFTs.
- The ChainlinkOracleDispatch and UniswapTwapOracleDispatch contracts are used to gather secure and tamper-resistant pricing data to be used by the protocol.
- The Chainlink contract uses Chainlink price feeds to pull aggregated data from off-chain.
- The Uniswap TWAP oracle contract uses the time-weighted average price of an asset over a period of time.
Yearn and AAVE InterestHandler:
- These contracts are used to determine interest rates on the AAVE and Yearn protocols and register token deposits to support the use of yTokens and aTokens in the protocol.
- Only the Token Vault contract has the ability to register deposits.
- The 'historic' and 'asset' are unused input variables in the getPrincipalDetail function and can be safely removed.
RevestAddressRegistry & RevestAccessControl:
- These contracts contain modifiers and getter functions to retrieve the addresses of contracts with elevated permissions in the ecosystem.
- The owner of the contract can update the contracts used in the address registry at any time.
Security Best Practices:
- The project uses Solidity 0.8, which has built-in checks to prevent overflow issues.
- Unit tests are included in the repository.
- Good structuring of logic to prevent reentrancy attacks; and usage of a custom reentrancy guard solution in all applicable functions.
- The contracts comply with the relevant Ethereum standards (ERC20, ERC1155).
External Threat Results
|Arbitrary Storage Write||N/A||PASS|
|Delegate Call to Untrusted Contract||N/A||PASS|
|Dependence on Predictable Variables||N/A||PASS|
|External Service Providers||N/A||PASS|
|State Change External Calls||N/A||PASS|
|User Supplied Assertion||N/A||PASS|
|Critical Solidity Compiler||N/A||PASS|
|Overall Contract Safety||PASS|