Boundless

Smart Contract Audit Report

Audit Summary

Boundless Audit Report Boundless is a decentralized capital fund which allows users to stake to earn rewards with additional benefits earned through their NFTs.

For this audit, we reviewed the project team's production folder at commit 4c313cf2f786fda437608d6697e9de8606032fe5 on the team's private GitHub repository.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: March 10th, 2022.
Updated: March 29th to address changes from commit d42184e5534cd36e554940e35de9345782921edd to commit 4c313cf2f786fda437608d6697e9de8606032fe5.

Finding #1 - sBND - High (Resolved)

Description: Users' balances are calculated using a multiplier based on their highest level BOUNDLESS:LEVELS NFT, regardless of how many rebases they have had the multiplier.
Exploit Scenario:
  1. User A stakes 5 BND tokens and is transferred 5 sBND tokens.
  2. A rebase occurs increasing the total supply per level.
  3. User B has a leveled NFT.
  4. User B also stakes 5 BND tokens and is transferred 5 sBND tokens.
  5. As a rebase occured and User B has a leveled NFT, his balance based on the balanceOf function is actually 6 tokens.
  6. User B unstakes his 6 sBND tokens for 6 BND tokens.
  7. There are now only 4 BND tokens in the staking contract, so user A has effectively lost 1 token.
Risk/Impact: Users can inflate their token balance through a high level BOUNDLESS:LEVELS NFT. This may lead to users being unable to unstake for BND tokens, effectively losing the BND tokens.
Recommendation: Users should accumulate additional sBND tokens over time and should be manually claimed by users.
Resolution: The team has prevented users from being unable to unstake by minting any additional tokens needed when unstaking.

Finding #2 - BoundlessStaking - High (Resolved)

Description: The unstakingTax() function underflows if more time than the unstakingTaxPeriod has passed since a user's last stake. This will cause a denial of service when trying to unstake due to SafeMath causing a revert.
uint _lastStake = lastStake[_recipient];
uint _taxTimePassed;
if (block.timestamp > _lastStake) _taxTimePassed = block.timestamp - _lastStake;
uint _taxTimeLeft = unstakingTaxPeriod - _taxTimePassed;
Risk/Impact: Users will be unable to withdraw if they have staked longer than the unstakingTaxPeriod. Users will have to stake again to withdraw, meaning they are unable to unstake without paying an unstaking tax.
Recommendation: If more time than the unstakingTaxPeriod has passed since a user's last stake, the unstaking tax should return 0.
Resolution: The team has implemented the recommendation described above.

Finding #3 - BoundlessPoints - High (Resolved)

Description: The balanceOf() function contains a loop that will always underflow and cause an out of bounds array access error.
for (uint256 i = pointsSpend[who].length - 1; i >= 0; i--) {
      PointsSpend memory _currentSpend = pointsSpend[who][i];
      if (_currentSpend.spendTimestamp > (block.timestamp - burnStopPeriod)) _spend = _spend + _currentSpend.amount;
}
Risk/Impact: Users will be unable to spend any of their pBND tokens.
Recommendation: The loop should break once it reaches the 0th index.
Resolution: The team has removed this logic in favor of using the BoundlessPointsVault contract.

Finding #4 - TaxProcessor - High (Resolved)

Description: The spaceCapsuleAddress variable is not initialized and cannot be initialized anywhere in the contract.
Risk/Impact: Any tokens collected as taxes and sent to the spaceCapsuleAddress will be lost.
Recommendation: The spaceCapsuleAddress should be initialized in the constructor.
Resolution: The team has implemented the recommendation described above.

Finding #5 - BoundlessPoints - Medium (Resolved)

Description: The balanceOf() function contains a loop that will underflow if a user's staking tier was never 0 and cause an out of bounds array access error.
for (uint256 i = actions.length - 2; i >= 0; i--) {
	PointsAction memory _record = actions[i];
	if (_record.stakingTier == 0) {
	  compoundingStartRecord = _record;
	  compoundingStartIndex = i;
	  break;
	}
}
Risk/Impact: Users will be unable to spend any of their pBND tokens if their staking tier was never 0.
Recommendation: The loop should break once it reaches the 0th index.
Resolution: The team has removed this logic in favor of using the BoundlessPointsVault contract.

Finding #6 - Ownable - Low (Resolved)

Description: The Ownable implementation being used does not set the _newOwner variable to the 0x0 address when a new owner is set.
Risk/Impact: If a new owner is set and subsequently relinquishes ownership, they will be able to regain ownership through the pullManagement() function. This issue impacts the sBND, BoundlessBackingTreasury, BoundlessInvestingTreasury, BoundlessStaking, BoundlessStandardBond, BoundlessPoints, BoundlessERC20, and NFTFactory contracts.
Recommendation: The _newOwner variable should be set to the 0x0 address in the pullManagement() and renounceManagement() functions.
Resolution: The team has implemented the recommendation described above.

Finding #7 - Policy - Low (Resolved)

Description: The Policy implementation being used does not set the _newPolicy variable to the 0x0 address when a new Policy address is set.
Risk/Impact: If a new Policy address is set and subsequently renounces ownership, they will be able to regain ownership through the pullPolicy() function. This issue impacts the Distributor and TaxProcessor contracts.
Recommendation: The _newPolicy variable should be set to the 0x0 address in the pullPolicy() and renouncePolicy() functions.
Resolution: The team has implemented the recommendation described above in the TaxProcessor contract and elected to no longer use the Distributor contract.

Finding #8 - BoundlessInvestingTreasury - Informational

Description: The backingTreasury address is not used anywhere within the contract.
Risk/Impact: This variable will contribute to deployment gas cost.
Recommendation: This variable can be safely removed.
Resolution: The team has implemented the recommendation described above.

Contracts Overview

BoundlessERC20 Contract:
  • This contract defines BND tokens which are the core token for the Boundless Money platform.
  • Users receive BND tokens by depositing capital in the form of another approved token in the BoundlessStandardBond contract.
  • The BoundlessBackingTreasury contract may mint any amount of BND to any address at any time.
  • Users may only transfer tokens when transfers are enabled.
  • Users may burn any of their tokens, or tokens they are approved to spend, at any time; tokens may also be sent to the 0x..dead address to lower the circulating supply, if desired.
  • For non-excluded addresses, there is a tax on token buys and sells, which is handled in the TaxProcessor contract.
  • The owner and this contract are excluded from fees on deployment.
  • The owner may enable and disable transfers at any time.
  • The owner may update the TaxProcessor address at any time.
  • The owner may add and remove addresses from the excluded list at any time.
  • The owner may update the valid Decentralized Exchange (Dex) pairs at any time.
  • The owner may update the BoundlessBackingTreasury contract address at any time.
  • The contract implements the EIP-2612 standard in order to support permits which allows for gasless approvals to be made via signatures.
  • The contract complies with the ERC-20 token standard, all standard functionality is present.
sBND Contract:
  • This contract defines the sBND token which users receive in return for staking BND tokens.
  • The initial total supply of 5,000,000 sBND is minted to the BoundlessStaking contract upon deployment.
  • There are no burn functions present, but anyone can send their tokens to the 0x..dead address at any time, which will reduce the circulating supply.
  • Only the BoundlessStaking contract address can trigger a rebase, which will increase the total supply based on the emissions from the BoundlessStaking contract.
  • The newly minted tokens are distributed amongst holders in a frictionless manner based on the user's highest level of BOUNDLESS:LEVELS NFT, where a higher level NFT corresponds to a larger portion of the rebase.
  • Due to the scaling rebases, it will be impossible to determine a true total supply. This will have an affect on tokenomics on blockchain explorers.
  • The sBND token should not be added as part of a token pair for any liquidity pool; if the team decides to create a liquidity pool, the sync() function will have to be called on the liquidity pool in order to absorb any tokens earned from the rebase into the liquidity pool.
  • The owner may update the proportion of each rebase that corresponds to each level of NFT at any time.
  • The owner may add new values to the boost proportions at any time.
  • The owner may update the gBND contract, Points contract, and the BoundlessLevels contract addresses at any time.
  • The contract implements the EIP-2612 standard in order to support permits which allows for gasless approvals to be made via signatures.
  • The contract complies with the ERC-20 token standard.
gBND Contract:
  • This contract defines the gBND token.
  • Users receive gBND tokens by wrapping sBND tokens in the BoundlessStaking contract and may similarly unwrap the gBND tokens to receive the original sBND tokens.
  • The "approved" address may mint any amount of gBND tokens to any address at any time.
  • The approved address may burn any amount of gBND tokens from any address at any time.
  • For non-excluded addresses, there is a tax on token buys and sells, which is handled in the TaxProcessor contract.
  • The owner and this contract are excluded from fees on deployment.
  • Each gBND token additionally represents votes intended to be used in a DAO where one token represents one vote.
  • Users must delegate their votes to themselves in order to to exercise their ability to vote.
  • Users may delegate their votes to another address allowing them to vote on behalf of the user.
  • Once votes are delegated, the user must explicitly delegate back to themselves to regain their votes.
  • Once after deployment, the approved address may update its own address and the sBND address.
  • The owner may update the approved, sBND, and staking addresses at any time.
  • The owner may add and remove any address from the fee excluded list at any time.
  • The owner may add and remove any address as an LP pair at any time.
  • The contract complies with the ERC-20 token standard, all standard functionality is present.
BoundlessPoints Contract:
  • This contract defines the BP token. BP tokens are used to boost BOUNDLESS:LEVELS NFTs.
  • Any valid Points Manager address may mint any number of BP tokens to any address at any time.
  • Only a valid Points Manager may spend/burn BP tokens on behalf of users.
  • BP tokens may not be transferred.
  • BP tokens are earned by locking gBND tokens within the BoundlessPointsVault contract.
  • The owner may add and remove an address as a Points Manager at any time.
  • The owner may update the Bond value per point to any value at any time.
  • The owner may update the price feed address at any time.
BoundlessPointsVault Contract:
  • When enabled, any user may lock their gBND tokens in this contract in order to earn BP tokens over time.
  • Users must specify a valid length of time in days that has been given a multiplier by the team, or the locking will fail.
  • Users will earn a maximum amount of points based on the amount of gBND locked, the length of time tokens are locked, and the multiplier associated with the locking period.
  • When enabled, users may unlock their tokens at any time.
  • An additional burn and tax fee will be taken if the tokens are withdrawn from the lock early. The burn fee will be burned and the tax fee will be sent to the BoundlessBackingTreasury contract.
  • If the total vesting period has passed, the lock will be "redeemed" for the full amount of rewards in BP tokens.
  • Users also have the option to redeem any of their locks at any time, despite the lock duration not being passed. In this case, users will receive a percentage of the total BP tokens rewards corresponding to the percentage of the lock duration has passed.
  • The remaining rewards will stay within the lock.
  • The owner may update the BND, gBND, sBND, price feed, staking, and backing treasury addresses at any time.
  • The owner may enable and disable locking and unlocking separately at any time.
  • The owner may set the burn and tax fees to any amount up to 100 percent at any time.
BoundlessAliens Contract:
  • A maximum of 555 BOUNDLESS:ALIENS NFTs may be minted.
  • When minting is enabled and during the sale period, whitelisted users who do not already own an NFT may mint themselves one NFT.
  • NFT metadata that contains information about the NFTs is stored using an off-chain URI endpoint.
  • The specified Proxy Registry address is given approval for all NFTs. This allows them to transfer NFTs on the behalf of users at any time.
  • The owner may mint themselves any number of NFTs at no cost at any time.
  • The owner may airdrop an NFT to any number of addresses at any time.
  • The owner may add and remove addresses from the whitelist at any time.
  • The owner may update the minting cost at any time.
  • The owner may update the max supply to any value greater than the current total supply.
  • The owner may update the sale duration at any time.
  • The owner may start the sale at any time.
  • The owner may enable and disable minting at any time.
  • The owner may withdraw any ETH in the contract at any time.
BoundlessLevels Contract:
  • When minting is enabled, any user may mint a BOUNDLESS:LEVELS NFT.
  • Each NFT has a level which corresponds to benefits within the platform.
  • The minting cost of each NFT corresponds to the level of NFT being minted.
  • Each level of NFT similarly has its own maximum supply.
  • Users may burn any of their NFTs at any time.
  • The NFTFactory address may mint any user any level of NFT at any time. If the minting would exceed the maximum supply, the maximum supply is raised to the new value.
  • NFT metadata that contains information about the NFTs is stored using an off-chain URI endpoint.
  • The owner may update the minting cost per level and the maximum supply for all levels at any time.
  • The owner may increase the maximum supply of each level at any time.
  • The owner may enable and disable minting at any time.
  • The owner may update the NFTFactory and sBND address at any time.
  • The owner may update the URI endpoint at any time.
  • The owner may withdraw any ETH in the contract at any time.
NFTFactory Contract:
  • This contract is used to raise the level of BOUNDLESS:LEVELS NFTs.
  • Each BOUNDLESS:LEVELS NFT has a "boost" level in addition to its regular level as an NFT; each NFT level has a corresponding maximum boost level.
  • Users may boost any of their NFT's level, up to the maximum, by using points accumulated in the BoundlessPoints contract.
  • Users may then use their boosted NFTs to mint a new NFT with a higher level.
  • Each level requires a certain number of NFTs with the maximum amount of boost of one lower level to be minted.
  • Users will burn the lower level NFTs in the process and be minted an NFT of one higher level in return.
  • The owner may update the BoundlessPoints contract and BoundlessLevels contract addresses at any time.
  • The owner may update the max boost capacity for each level at any time.
  • The owner may update the number of NFTs required to upgrade each level at any time.
BoundlessStandardBond Contract:
  • When deposits are enabled and the contract has been initialized, any user may deposit a specified "principle" token into this contract to purchase a Bond and receive rewards over time in the form of sBND tokens.
  • Users will receive more BND tokens and a larger sBND payout from the Bond based on the calculated value of the amount of principle deposited relative to the current BND price and the current discount value.
  • The BND price is calculated using off-chain pricing information from a custom pricing Oracle. We did not review the pricing Oracle and are unable to give an assessment in regards to security.
  • The calculated payout must be larger that 0.01 sBND and less than the maximum payout.
  • The principle is deposited into the BoundlessBackingTreasury contract which either mints or transfers BND in return to this contract.
  • An additional percentage of the payout is received from the BoundlessBackingTreasury contract, which is subsequently transferred to the TaxProcessor contract where it is distributed as Bond rewards proceeds.
  • The received BND is staked for the user into the BoundlessStaking contract where they will receive sBND tokens in return.
  • The total time required to receive all sBND rewards is decreased in relation to the highest level of BOUNDLESS:LEVELS NFT the user possesses.
  • When redeeming the Bond for rewards, users will receive a percentage of the total sBND rewards relative to the percentage of total vesting time has passed.
  • The discount variable is set upon deployment and will be increased or decreased by the adjustment rate after every deposit, given that the adjustment rate is set and the adjustment buffer has elapsed since the last adjustment.
  • The discount will be adjusted until it reaches the target adjustment value.
  • The initial total debt is set upon deployment and increases by the value of the deposited principle during each deposit.
  • Additionally, at the beginning of each deposit the total debt is "decayed" and correspondingly decreased.
  • The amount of debt that is decayed is based on the time since the last decay and the default vesting term for Bonds.
  • The total debt will continuously decay towards but will never reach 0.
  • The owner may withdraw any non-BND token or non-principle token from the contract to the TaxProcessor contract at any time.
  • The owner may initialize the contract, setting the discount, vesting term, max payout, fees, max debt, total debt and last decay timestamp, at any time.
  • The owner may set the vesting term to any value greater than 36 hours at any time.
  • The owner may set the fee to any value up to 100% at any time.
  • The owner may set the max debt and minimum price in USD at any time.
  • The owner may set the max payout at any time. The OlympusBondDepositoryOracle max payout may only be set to 1% of the total supply of BND tokens at most.
  • The owner may enable and disable deposits at any time.
  • The owner may update the address of the Oracle used for pricing at any time.
BoundlessBackingTreasury Contract:
  • Any approved Reserve Depositor may deposit any approved Reserve Token into the contract and receive a specified amount of BND tokens in return.
  • Any approved Liquidity Depositor may deposit any approved Liquidity Token into the contract and receive a specified amount of BND tokens in return.
  • If the contract has sufficient balance, the depositor will be transferred the BND tokens from the contract.
  • If the contract does not have sufficient balance, they will be minted instead.
  • The deposited tokens are sent to the TaxProcessor contract where they are distributed as rewards from principle proceeds.
  • Any approved Reserve Spender may withdraw any approved Reserve Token from the contract by burning BND at a 1:1 ratio.
  • Any approved Reward Manager may mint or transfer from the contract a specified number of BND tokens to any address at any time.
  • Any approved Liquidity Manager may withdraw any approved Liquidity Token from the contract at any time.
  • Any approved Reserve Manager may withdraw any token that is not an approved Liquidity Token from the contract at any time.
  • The team should exercise caution when adding addresses as Reserve and Liquidity depositors as they are able to mint any amount of BND at any time.
  • The owner may queue an update to the Liquidity Depositors and Managers, the Reserve Depositors and Managers, approved Liquidity and Reserve tokens, and Reward Managers at any time.
  • After the wait time has elapsed, the Manager must manually trigger the toggle() function in order to add the queued address to the appropriate list.
  • The owner may update the wait time before updating at any time.
  • The owner may update the TaxProcessor address at any time.
BoundlessStaking Contract:
  • Any user may use this contract to stake BND tokens in return for sBND tokens at a 1:1 ratio.
  • When staking, a rebase is automatically triggered if the current epoch has not passed.
  • A rebase may also be manually triggered, and optionally triggered when unstaking, if the current epoch has not passed.
  • The rebase will mint a specified emission percentage of the total supply of BND tokens, and subsequently stake them for new sBND tokens which are distributed frictionlessly amongst holders.
  • The emission percentage is set upon deployment and will be increased or decreased by the adjustment rate after every successful rebase, given that the adjustment rate is set.
  • The emission percentage will be adjusted until it reaches the target adjustment value.
  • Non-excluded users will pay a tax when unstaking.
  • The tax starts at 30% and decreases over time until reaching 0 at 7 days.
  • The tax is reset to 30% each time a user stakes.
  • Any tax collected will be sent to the BackingTreasury.
  • Users may also use this contract to wrap their sBND and receive gBND at a 1:1 ratio minus a tax.
  • The tax for wrapping sBND is calculated in the same way as the unstaking tax.
  • Users may unwrap their gBND to receive sBND at a 1:1 ratio, burning the gBND in the process.
  • The owner may set the emission percentage and adjustment rate at any time.
  • The owner may update the BackingTreasury address at any time.
  • The owner may update the initial unstaking tax, and when the tax reaches 0 to any value at any time.
  • The owner may exclude and include any address from taxes at any time.
  • The Randomizer may set the epoch end time to any value within a deviation threshold of the standard rebase time at any time.
  • The owner may update the Randomizer address at any time.
TaxProcessor Contract:
  • This contract is used to collect and distribute taxes among various addresses depending on the manner in which they were collected.
  • Taxes that are taken from the principle used to purchase Bonds are distributed in two manners.
  • The SpaceCapsule, Marketing, and Dev addresses are each distributed a portion of the taxedReservesSpent.
  • The InvestingTreasury and BackingTreasury addresses are each distributed a portion of the depositAmount.
  • Taxes that are taken as Bond rewards proceeds are split between the Marketing and Dev addresss.
  • Taxes collected from BND buys and sells are shared between the BackingTreasury and Airdrop addresses. Additionally the burn portion is burned.
  • This contract is also used to calculate the buy and sell tax on BND transfers.
  • Users pay taxes on buys and sells per 24 hour period. Taxes are taken as a percentage of the total amount bought or sold each period and are reset after 24 hours.
  • The tax scales with amount, where the larger the amount the larger the tax.
  • Users also receive a discount on taxes according to their highest level BOUNDLESS:LEVELS NFT, or receive a flat tax discount if they are the owner of a BOUNDLESS:ALIENS NFT.
  • The owner may update the tax rate to any value up to 25% at any time.
  • The owner may update the tax period to any value up to 36 hours at any time.
  • The owner may set the tax discount per NFT level and the flat BOUNDLESS:ALIENS NFT discount up to 50% at any time.
  • The owner may update the reduction in Bond vesting time per NFT level to any value at any time.
  • The owner may update the portion for each address when distributing Bond principle rewards at any time.
  • The owner may update the burn portion for BND buy and sell taxes at any time.
  • The owner may enable and disable BND buy and sell taxes at any time.
  • The owner may update the BackingTreasury, InvestingTreasury, Aliens NFT, and Levels NFT addresses at any time.
  • The owner may update the Price Feed Oracle address at any time.
BoundlessInvestingTreasury Contract:
  • Any approved Asset Manager may withdraw any amount of any token from the contract at any time.
  • The owner may queue an update to the Asset Manager at any time.
  • After the wait time has elapsed, the Manager must manually trigger the toggle() function in order to add the queued address to the appropriate list.
  • The owner may update the wait time before updating at any time.
  • The owner may update the BackingTreasury address at any time.
BondingCalculator Contract:
  • This contract is intended to be used to provide a valuation for liquidity tokens.
  • The valuations are derived by doubling the square root of Uniswap's constant product for reserves in order to account for both assets in the liquidity pair.
  • A portion of the total valuation is returned based on the proportion of LP tokens the user is looking to value relative to the total supply of the LP tokens.

Audit Results

Vulnerability CategoryNotesResult
Arbitrary Jump/Storage WriteN/APASS
Centralization of Control
  • The team retains ownership controls described above.
  • Contract addresses where user's funds are sent can be updated.
  • There are instances of uncapped fees.
  • Pricing data relies on off-chain logic.
  • The team may initialize the Bond contract multiple times changing all of the economic variables at once.
WARNING
Compiler IssuesN/APASS
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Ether/Token TheftN/APASS
Flash LoansN/APASS
Front RunningN/APASS
Improper EventsN/APASS
Improper Authorization SchemeN/APASS
Integer Over/UnderflowN/APASS
Logical IssuesN/APASS
Oracle IssuesN/APASS
Outdated Compiler VersionN/APASS
Race ConditionsN/APASS
ReentrancyN/APASS
Signature IssuesN/APASS
Unbounded LoopsN/APASS
Unused CodeN/APASS
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 1000+ 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.