BiShares Finance - Audit Report
BiShares Finance is building an innovative platform to easily build, deploy, and maintain Index Funds comprised of various tokens in a secure and decentralized manner.
We reviewed BiShares Finance's contracts in the following private Github repositories:
- BiShares dETF at commit ea3ada651d7a68ae6788471bf3b7be3ceb6fa810
- BiShares DEX Core at commit 57de853d4747475119939f7061ceba321339ec24
- BiShares DEX Periphery at commit 290b9b6191106cb97e2ebdd7daa12dc63c39b0d4
- BiShares Farms at commit 7cc6fa150413dab3afb74dba2326ed8f2a6ebc36
Audit Findings Summary:
Notes on Individual Contracts:
- No security issues from outside attackers were identified.
- The project team should exercise caution to avoid adding fee-on-transfer tokens as an asset in any Index Pool which can lead to errors during balance calculations.
- Investing requires placing trust in the project team as they have substantial power in the ecosystem.
- Date: October 12th, 2021.
- Updated: November 28th, 2021 to remove the UnboundTokenSeller contract entirely as well as logic in the IndexPool contract related to swapping and flash-borrowing funds in the pool.
- This contract contract provides the owner a few permissions across the platform.
- The owner can use this contract to configure and deploy an Index Pool.
- The system uses time-weighted averages to determine the pricing of each token involved in the Index Pool.
- Once an Index Pool is deployed, the PoolInitializer must finish preparing the pool by calculating the weights for each token in the Pool.
- Anyone is able to use the contract to evaluate and update data including denormalized weights for an Index Pool that has reached it's minimum balance requirements at any time.
- Anyone is able to reindex any initialized Index Pool at any time, as long as it is due for a reindex; reindexing recalculates and updates the minimum required balances and the denormalized weights for each token in the Index Pool.
- Anyone is able to reweigh any initialized Index Pool at any time, as long as it is due for a reweigh; reweighing recalculates the denormalized weights for the desired tokens in the Index Pool, as well as the top tokens in the category of interest.
- The owner is able to set the maximum number of different tokens any initialized Index Pool can be comprised of at any time.
- The owner is able to set the exit fee receiver address on any initialized Index Pool at any time.
PoolFactory and PoolFactoryAccessControl:
- Anyone is able to trigger an update for the prices in a specified category at any time; the contract uses time-weighted averages using data from Uniswap.
- Anyone is able to trigger a category re-ordering which uses in-place insertion sort to optimally sort the tokens by descending order in terms of market cap.
- The owner is able to create a new token category at any time.
- The owner is able to add any token to any category at any time, as long as the amount of tokens in a category is under the maximum of 25.
- The owner is also able to remove any token from any category at any time.
- Approved addresses can use this contract to deploy a new Index Pool using a many-to-one proxy, meaning there can be multiple addresses for the same implementation code; this is used in order to create multiple Index Pools at will.
- The owner of the PoolFactory contract is intended to be the PoolFactoryAccessControl contract.
- The owner is able to use the PoolFactoryAccessControl contract to transfer the ownership of the PoolFactory contract to any address at any time.
- The owner is able to grant or revoke Admin access from any address at any time.
- The owner or any address with the Admin role is able to grant the ability for any address to deploy pools via the Pool Factory contract at any time; only the owner can revoke this permission.
- The project team can use the PoolInitializer contract to initialize an IndexPool via the MarketCapSqrtController contract.
- The Index Pool is not considered to have finished initialization until it has met the minimum contribution amounts for each of the desired tokens; the minimum contribution amounts are based on the weights of each token.
- Users can contribute tokens to the Index Pool in order to meet the balance requirements; the system will assign the users "credits" based on the current ETH value of the tokens being deposited.
- Before contributing, users can issue a call to fetch the latest time-weighted average token prices to ensure the most up-to-date pricing.
- Once the Index Pool has met the target contribution amounts for each token in its composition, the initialization is marked as finished, and the Controller contract calculates the denormalized weights for each token based on the current ETH value of the balance of each token.
- Once the initialization of the Index Pool is completed, users will be able to claim a portion of the Index Pool shares based on the ETH value of their tokens at contribution time.
- In order to join the Pool, a user must deposit an amount of each asset token the Pool is comprised of proportional to the amount of shares the user wishes to purchase; users will be able to join as long as the Pool has not yet reached its capacity.
- Upon exiting the Pool, users pay a portion of their shares to the exit fee recipients determined by the project team. The remaining shares are liquified and an amount of each asset token proportional to the amount of shares being burned is delivered to the user.
- The MarketCapSqrtController contract is able to set the maximum pool shares cap to any value at any time.
- The MarketCapSqrtController contract is able to set both exit fee recipient addresses to any address at any time.
- The MarketCapSqrtController contract is able to set the minimum balance required for a token in the pool as long as the token is bound and not in Ready status.
- Anyone can use the BisharesUniswapRouterMinter contract to invest in the Index Pool using a single asset; the tokens or ETH deposited are swapped for the asset tokens of the Index Pool and the user receives shares representing their ownership of the Index Pool in exchange.
- Anyone can use the BisharesUniswapRouterBurner contract to divest from the Index Pool and receive a single asset token or ETH in return. An amount of each asset token the Index Pool is comprised of proportional to the amount of shares the user wishes to burn is swapped for the desired output token and delivered to the user.
- The Bishares DEX enables users to create liquidity pairs and swap tokens and is based on the battle-tested Uniswap codebase.
- The Factory contract will allow users to create liquidity pairs for any token, thereby enabling trading on the platform.
- The Router contract routes orders to the user-determined pair contract to swap assets.
- The liquidity provider fee for token swaps is 0.3% of the value transacted.
- When dealing with tokens that have a fee-on-transfer, the estimated output does not properly subtract the fee. As a result, users of fee-on-transfer tokens must set a slippage percentage prior to executing trades.
- The contracts utilize SafeMath to prevent overflow issues.
General notes across all contracts:
- This contract allows anyone to stake a single asset determined by the project team in exchange for rewards.
- On initialization, the owner must specify the rewards token, the staking token, rewards rate, and the start/end blocks of the staking period; after the end block, rewards will no longer be distributed.
- The team must exercise caution when deciding the staking token to avoid fee-on-transfer and ERC777-compliant tokens (this is uncommon).
- Users will receive a reward amount on each block based on the amount staked; staking rewards can be calculated and transferred to the user at any time.
- On deposits and withdrawals, pending rewards are calculated and transferred.
- On withdrawals, the user will receive the desired amount of the staking tokens; this amount cannot be more than the amount the user has deposited.
- The user can also trigger an emergency withdraw, which will transfer all the user's deposited LP tokens to their wallet address, without calculating rewards.
- The owner is able to withdraw the reward tokens from the contract at any time.
- The owner is able to set the end block and stop reward distribution at any time.
- The team worked with us to resolve logical issues and implement changes related to code optimizations.
- Excellent structuring of logic to prevent reentrancy attacks and to optimize gas usage.
- The platform uses Uniswap to calculate time-weighted averages for pricing data; price data points consulted in the average are at least 30 minutes apart.
- The Index Pool and BMath libraries used throughout the platform are based on code pioneered by Balancer Finance.
- The BMath libaries also serve to protect transactions from overflows.
- The contracts utilize 112x112 fixed point number representation for arithmetic operations, which promotes gas efficiency in calculations, but sacrifies range and precision that the standard floating point number representation offers.
External Threat Results
|Arbitrary Storage Write||N/A||PASS|
|Delegate Call to Untrusted Contract||N/A||PASS|
|Dependence on Predictable Variables||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|
Contract Source Summary and Visualizations