Smart Contract Audit Report
Arcadia Finance is building a non-custodial lending platform in which users can deposit multiple different assets to be used as collateral.
For this audit, we reviewed Arcadia Finance's select contracts at commit 89ac99111735e87783fea814ff712d5feb07267d on the team's private GitHub repository.
The team has elected to acknowledge and leave one low finding unresolved. In addition, centralized aspects are present.
Date: June 9th, 2022.
Updated: June 10th, 2022 to reflect changes from commit 539d51380c308f1a41ec585cd16cb33b4bb06e4d to commit 89ac99111735e87783fea814ff712d5feb07267d.
Finding #1 - Factory - Medium (Resolved)Description: The
safeTransferFrom(address from, address to, uint256 id, bytes memory data)function is not overridden in the Factory contract.
Risk/Impact: A Factory NFT's corresponding Vault ownership will not be transferred with the NFT if this function is called.
Recommendation: The team should override this function to transfer Vault ownership.
Resolution: The team has implemented the above recommendation.
Finding #2 - Vault - LowDescription: The number of blocks passed is currently used to calculate yearly interest. This will likely result in an inaccurate measure of time.
Risk/Impact: Yearly interest will not be calculated or displayed accurately.
Recommendation: The team should use block.timestamp to calculate interest rates.
Update: The team has acknowledged and elected not to address this finding.
Finding #3 - Liquidator - Informational (Resolved)Description: The auctionDuration variable is not declared constant, but cannot be updated.
Recommendation: The auctionDuration variable can be declared constant to save gas on deployment and each reference.
Resolution: The owner can now update this variable at any time. As a result, it should no longer be declared constant.
StandardERC20SubRegistry, FloorERC721SubRegistry, & FloorERC1155SubRegistry Contracts:
- As the contracts are implemented with Solidity v0.8.x, they are safe from any possible overflows/underflows.
- The team should exercise caution when adding supported collateralizable assets and add only trusted tokens.
- These contracts are used by the owner to register assets to the platform, and are used by the MainRegistry contract.
- The owner can add any token to the StandardERC20Registry contract's asset list at any time.
- The owner can add a range of accepted IDs from an ERC721 contract to the FloorERC721SubRegistry contract's asset list at any time.
- The owner can add an specific ID from an ERC1155 contract to the FloorERC1155SubRegistry contract's asset list at any time.
- When an asset is added, a list of oracles are provided to be used to fetch the asset's exchange rate to USD or a different numeraire.
- The asset is also added to the associated MainRegistry contract.
- The owner can update any asset's information at any time, including its decimals, ID range, and oracles.
- The owner can remove an asset from the asset list at any time.
- This contract is used to fetch values and credit ratings of registered assets.
- The contract will contain a list of assets and numeraires.
- Each asset will have a credit rating in relation to each numeraire; these credit ratings will influence a user's interest rate.
- The owner can add a numeraire to the numeraire list at any time. The numeraire will also be added to the Factory contract.
- The owner of the associated SubRegistry contracts can add a new asset to this contract at any time.
- When a numeraire or asset is added, credit ratings are provided so that each asset has a credit rating in relation to each numeraire.
- Each numeraire's information is stored in this contract, including its USD Oracle, corresponding Stable token address, and decimals.
- The owner can add a SubRegistry to the SubRegistry list at any time.
- The owner can update the Factory address at any time.
- The owner can update any asset in the contract unless the assetsUpdatable flag has been disabled.
- Once the assetsUpdatable flag has been disabled, it cannot be reenabled.
- This contract retrieves the price of a specified asset in the form of USD or a specified numeraire using a series of Chainlink Oracles.
- When fetching a rate, a list of Oracle addresses and a numeraire are provided.
- Each supported Oracle's information is stored in this contract which includes its base asset, quote asset, decimals, base asset numeraire, and whether the base asset is a numeraire.
- The rate is calculated by converting the rates along the path, as each Oracle's base asset is intended to be equal to the following Oracle's quote asset.
- If an Oracle along the path's base asset is "0" and determined to be a numeraire, it will be treated as the USD rate and returned.
- If an Oracle along the path's base asset is the passed numeraire and determined to be a numeraire, the rate in the numeraire will be returned.
- The team must exercise caution to ensure that the correct Oracles are registered.
- The owner can add a new Oracle with specified information at any time.
- This contract allows users to create and liquidate Vaults.
- When a user creates a new Vault contract through the Factory, they will specify the numeraire to be used for the Vault.
- The associated Proxy contract will initialize the Vault using its implementation code, which is set once by the owner.
- The Vault's Registry address, logic, Stake contract, and InterestRateModule contract are all initialized based on this contract's specified settings.
- Upon creation, the user is also minted a Factory NFT with an ID corresponding to the new Vault.
- When the NFT is transferred, ownership of the Vault is transferred with it.
- Users can also initiate liquidation of eligible Vaults through this contract, which will transfer the Factory NFT and Vault ownership from the owner of the Vault to the Liquidator contract.
- The owner can update Vault settings at any time, which will only affect Vaults created after the update.
- The owner can update the Liquidator address at any time.
- The owner can update the base URI at any time.
- This contract allows its owner to borrow funds by depositing various whitelisted assets to be used as collateral.
- Vaults can support ERC20, ERC721, and ERC1155 tokens.
- A user's total collateral value will increase depending on the tokens and amounts deposited.
- Each supported token will have an assigned credit rating and value, which is determined by its price in relation to the Vault's numeraire and its assigned risk.
- Supported tokens and their credit ratings are determined by the Vault's associated MainRegistry contract.
- In order to borrow or withdraw their funds, their resulting collateral to debt ratio must be larger than 150%.
- A user will also have a "yearly" interest rate based on the amounts and credit ratings of the user's deposited assets.
- A user's yearly interest rate is updated using the associated InterestRateModule contract each time a user withdraws, borrows, or repays their debt.
- Elapsed blocks are used to measure time passed. It will currently take longer than an actual year for the contract's calculated "year" to pass; this will become more accurate once the network merges to become proof-of-stake.
- Users can also manually update their yearly interest rate, which may be beneficial after depositing certain collateral.
- A user's debt is synced any time they borrow, repay, or manually update their interest rate.
- This will realize any accrued debt from interest and mint Stable tokens to the associated Staking contract.
- Any user can also manually sync a Vault's debt, which can potentially allow a Vault to be liquidated.
- When borrowing, the user is minted Stable tokens equal to the borrowed amount.
- When repaying debt, these tokens are burned and the user's debt is reduced.
- A user's interest rate will be reset to 0 if a user pays off their entire debt.
- If a user's collateral value falls below 110% of their debt, their Vault will become eligible to become liquidated.
- Users can only liquidate Vaults through the Factory contract, which will transfer the Vault to the Liquidator address and create a new auction for it.
- This contract is used to calculate yearly interest based on provided collateral amounts, each collateral's credit rating, and a user's current borrowed amount.
- Each collateral is given a credit rating, and each credit rating corresponds to its own interest rate.
- To calculate interest, the total value of collaterals for each credit rating are first fetched from the MainRegistry contract.
- The interest amount will then be calculated based on each collateral amount and its credit rating's specified interest rate.
- If a user has both low and high interest rate collateral, but meets their minimum collateral threshold with only their low interest collateral, the high interest collateral will not be calculated into the interest rate.
- A base interest rate is added to the calculated rate.
- The owner can update the base interest rate at any time.
- The owner can update the interest rate for a credit rating at any time.
- This contract is used to purchase listed liquidated Vaults and claim proceeds from sales.
- When a Vault is liquidated, its price is initially set to 110% of the Vault's debt, which is equal to its liquidation threshold.
- This price decreases over time until the Vault is purchased by a user.
- When purchasing, the buyer must pay in the form of the Vault's numeraire's corresponding Stable token, which is fetched by the Factory contract.
- If the Vault is sold for over 100% of the Vault's debt, Stable tokens equal to the amount of debt are burned from the buyer, and surplus is transferred to this contract.
- Otherwise, Stable tokens equal to the price of the Vault are burned from the buyer.
- The buyer is then transferred the Factory NFT of the Vault ID as well as ownership of the Vault, which contains any remaining collateral with no debt.
- Any time a Vault is liquidated, the liquidator is immediately granted 2% of the Vault's debt.
- If a Vault is sold for a price greater than its debt, remaining surplus up to 15% of the Vault's debt is granted to the Protocol Treasury address.
- If any remaining surplus still remains, it is granted to the Vault's previous owner.
- Users must manually claim their rewards, which are transferred in the form of the Vault's corresponding Stable token.
- If this contract does not hold enough funds to pay a claim, the remainder is "withdrawn" from the Reserve Fund address.
- As the Reserve Fund was not included in the scope of this audit, we are unable to provide an assessment with regards to security or functionality.
- The team must ensure that enough tokens exist in the Reserve Fund in order to pay claims.
- The owner can update the rate at which the price decreases to any amount at any time.
- The owner can update the Factory address, Protocol Treasury address, and Reserve Fund address at any time.
|Arbitrary Jump/Storage Write||N/A||PASS|
|Centralization of Control||WARNING|
|Delegate Call to Untrusted Contract||N/A||PASS|
|Dependence on Predictable Variables||N/A||PASS|
|Improper Authorization Scheme||N/A||PASS|
|Outdated Compiler Version||N/A||PASS|
|Overall Contract Safety||PASS|
Contract Source Summary and Visualizations
About Solidity Finance
Solidity Finance was founded in 2020 and quickly grew to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1300+ 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 across 1500 projects!.
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 Solidity Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A Solidity 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.