BeamswapV3
Smart Contract Audit Report
Audit Summary
Beamswap is building a new Decentralized Exchange.
For this audit, we reviewed the project team's BeamswapV3Factory, BeamswapV3Pool, and BeamswapV3PoolDeployer contracts at commit 6882b100ce04973de5f3c72713a9c9cad01770d3 on the team's private GitHub repository. The other contracts within the Beamswap repository were out of scope for the purpose of this audit, but the project team claims that they are copied directly from the UniswapV3 Core and Periphery repositories.
We previously reviewed the project team's token and staking contracts here.
Audit Findings
No findings were identified.
Date: May 19th, 2023.
Contracts Overview
BeamswapV3Factory Contract:BeamswapV3Pool Contract:
- Any user can use this contract to create a new pool by specifying two token addresses and a fee amount. The tick spacing value for the specified fee amount must have been previously set by the team.
- The newly created pool is deployed in the BeamswapV3PoolDeployer contract.
- The owner can set a tick spacing, between 1 and 16,383, for any fee amount up to 999,999 at any time.
- The owner can update the LM PoolDeployer address at any time.
- The owner or LM PoolDeployer address can set the LM Pool in the BeamswapV3Pool contract at any time.
BeamswapV3PoolDeployer Contract:
- This contract is used to facilitate token trading on the Beamswap platform.
- Any user can initialize the pool one time, which sets the initial price and tick, and prepares the first observation of the pool.
- Any user can increase the maximum number of price and liquidity observations that this pool will store at any time.
- Any user can mint an amount of tokens to a recipient address for a specified tick range in the pool as long as the calculated amount of liquidity for each token has been successfully added by the user.
- Any user can specify a number of tokens to burn from their position within a specified tick range at any time.
- The user receives an amount of each token in return, depending on the current price and the tick range of the position. The amounts of tokens returned are added to the tokens owed to the position, which can later be collected.
- Any user can collect any amount of currently owed tokens for their position within a specified tick range at any time. The collected tokens are sent to a specified recipient address.
- Any user can specify a number of tokens to swap within the liquidity pool at any time.
- The user will specify the direction of the swap, the price limit of the swap (within the contract's valid range), and a recipient address.
- During the swap, the amount of tokens to be swapped, the fees to be paid, and any changes in liquidity due to crossing initialized tick boundaries are calculated.
- If any protocol fees are accrued during the swap, they are added to the protocol's total accumulated fees.
- The calculated number of output tokens are sent to the recipient address.
- Rewards are automatically accumulated in the LM Pool contract on all swaps. The LM Pool contract was not included in the scope of this audit, so we are unable to provide an assessment with regards to its security or functionality.
- Any user can initiate a flash swap by specifying an amount of each token in the liquidity pool and a recipient address.
- The specified token amounts are transferred from the pool to the recipient address.
- The function verifies that the balances of the two tokens in the pool are at least as much as before the flash swap plus the respective fees in order for the transaction to successfully occur.
- If any tokens were paid back in excess of the required amount and fees, the excess tokens are divided based on the Fee protocol value and added to the protocol's total accumulated fees. The remaining tokens are distributed to the liquidity providers.
- The owner of the Factory contract can specify an amount of the contract's protocol fees to collect for each token at any time. The tokens are sent from the contract to the caller's wallet address.
- The owner of the Factory contract can set the fee protocol values for each token at any time. These values must be either set to 0 or between 4 and 10.
- The BeamswapV3Factory contract can update the LM pool address at any time.
- The Factory address can use this contract to deploy a new pool by specifying two token addresses, a fee amount, and a tick spacing value.
- A new BeamswapV3Pool contract is created during the deployment process.
- The deployer of this contract can set the BeamswapV3Factory address one time.
Audit Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Jump/Storage Write | N/A | PASS |
Centralization of Control | N/A | PASS |
Compiler Issues | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Ether/Token Theft | N/A | PASS |
Flash Loans | N/A | PASS |
Front Running | N/A | PASS |
Improper Events | N/A | PASS |
Improper Authorization Scheme | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | N/A | PASS |
Oracle Issues | N/A | PASS |
Outdated Compiler Version | N/A | PASS |
Race Conditions | N/A | PASS |
Reentrancy | N/A | PASS |
Signature Issues | N/A | PASS |
Sybil Attack | N/A | PASS |
Unbounded Loops | N/A | PASS |
Unused Code | N/A | PASS |
Overall Contract Safety | PASS |
BeamswapV3Factory Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IBeamswapV3Factory
- [Ext] owner
- [Ext] feeAmountTickSpacing
- [Ext] getPool
- [Ext] createPool #
- [Ext] setOwner #
- [Ext] setLmPoolDeployer #
- [Ext] enableFeeAmount #
- [Ext] setLmPool #
+ [Int] IBeamswapV3PoolDeployer
- [Ext] parameters
- [Ext] deploy #
+ NoDelegateCall
- [Pub] #
- [Prv] checkNotDelegateCall
+ [Int] IBeamswapV3PoolImmutables
- [Ext] factory
- [Ext] token0
- [Ext] token1
- [Ext] fee
- [Ext] tickSpacing
- [Ext] maxLiquidityPerTick
+ [Int] IBeamswapV3PoolState
- [Ext] slot0
- [Ext] feeGrowthGlobal0X128
- [Ext] feeGrowthGlobal1X128
- [Ext] protocolFees
- [Ext] liquidity
- [Ext] ticks
- [Ext] tickBitmap
- [Ext] positions
- [Ext] observations
+ [Int] IBeamswapV3PoolDerivedState
- [Ext] observe
- [Ext] snapshotCumulativesInside
+ [Int] IBeamswapV3PoolActions
- [Ext] initialize #
- [Ext] mint #
- [Ext] collect #
- [Ext] burn #
- [Ext] swap #
- [Ext] flash #
- [Ext] increaseObservationCardinalityNext #
+ [Int] IBeamswapV3PoolOwnerActions
- [Ext] setFeeProtocol #
- [Ext] collectProtocol #
- [Ext] setLmPool #
+ [Int] IBeamswapV3PoolEvents
+ [Int] IBeamswapV3Pool (IBeamswapV3PoolImmutables, IBeamswapV3PoolState, IBeamswapV3PoolDerivedState, IBeamswapV3PoolActions, IBeamswapV3PoolOwnerActions, IBeamswapV3PoolEvents)
+ [Lib] LowGasSafeMath
- [Int] add
- [Int] sub
- [Int] mul
- [Int] add
- [Int] sub
+ [Lib] SafeCast
- [Int] toUint160
- [Int] toInt128
- [Int] toInt256
+ [Lib] TickMath
- [Int] getSqrtRatioAtTick
- [Int] getTickAtSqrtRatio
+ [Lib] LiquidityMath
- [Int] addDelta
+ [Lib] Tick
- [Int] tickSpacingToMaxLiquidityPerTick
- [Int] getFeeGrowthInside
- [Int] update #
- [Int] clear #
- [Int] cross #
+ [Lib] BitMath
- [Int] mostSignificantBit
- [Int] leastSignificantBit
+ [Lib] TickBitmap
- [Prv] position
- [Int] flipTick #
- [Int] nextInitializedTickWithinOneWord
+ [Lib] FullMath
- [Int] mulDiv
- [Int] mulDivRoundingUp
+ [Lib] FixedPoint128
+ [Lib] Position
- [Int] get
- [Int] update #
+ [Lib] Oracle
- [Prv] transform
- [Int] initialize #
- [Int] write #
- [Int] grow #
- [Prv] lte
- [Prv] binarySearch
- [Prv] getSurroundingObservations
- [Int] observeSingle
- [Int] observe
+ [Lib] TransferHelper
- [Int] safeApprove #
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeTransferETH #
+ [Lib] UnsafeMath
- [Int] divRoundingUp
+ [Lib] FixedPoint96
+ [Lib] SqrtPriceMath
- [Int] getNextSqrtPriceFromAmount0RoundingUp
- [Int] getNextSqrtPriceFromAmount1RoundingDown
- [Int] getNextSqrtPriceFromInput
- [Int] getNextSqrtPriceFromOutput
- [Int] getAmount0Delta
- [Int] getAmount1Delta
- [Int] getAmount0Delta
- [Int] getAmount1Delta
+ [Lib] SwapMath
- [Int] computeSwapStep
+ [Int] IERC20Minimal
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IBeamswapV3MintCallback
- [Ext] beamswapV3MintCallback #
+ [Int] IBeamswapV3SwapCallback
- [Ext] beamswapV3SwapCallback #
+ [Int] IBeamswapV3FlashCallback
- [Ext] beamswapV3FlashCallback #
+ [Int] IBeamswapV3LmPool
- [Ext] accumulateReward #
- [Ext] crossLmTick #
+ BeamswapV3Pool (IBeamswapV3Pool, NoDelegateCall)
- [Pub] #
- [Prv] checkTicks
- [Int] _blockTimestamp
- [Prv] balance0
- [Prv] balance1
- [Ext] snapshotCumulativesInside
- modifiers: noDelegateCall
- [Ext] observe
- modifiers: noDelegateCall
- [Ext] increaseObservationCardinalityNext #
- modifiers: lock,noDelegateCall
- [Ext] initialize #
- [Prv] _modifyPosition #
- modifiers: noDelegateCall
- [Prv] _updatePosition #
- [Ext] mint #
- modifiers: lock
- [Ext] collect #
- modifiers: lock
- [Ext] burn #
- modifiers: lock
- [Ext] swap #
- modifiers: noDelegateCall
- [Ext] flash #
- modifiers: lock,noDelegateCall
- [Ext] setFeeProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] collectProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] setLmPool #
+ BeamswapV3Factory (IBeamswapV3Factory, NoDelegateCall)
- [Pub] #
- [Ext] createPool #
- modifiers: noDelegateCall
- [Ext] setOwner #
- [Ext] setLmPoolDeployer #
- [Pub] enableFeeAmount #
- [Ext] setLmPool #
- modifiers: onlyOwnerOrLmPoolDeployer
BeamswapV3Pool Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IBeamswapV3Factory
- [Ext] owner
- [Ext] feeAmountTickSpacing
- [Ext] getPool
- [Ext] createPool #
- [Ext] setOwner #
- [Ext] setLmPoolDeployer #
- [Ext] enableFeeAmount #
- [Ext] setLmPool #
+ [Int] IBeamswapV3PoolDeployer
- [Ext] parameters
- [Ext] deploy #
+ NoDelegateCall
- [Pub] #
- [Prv] checkNotDelegateCall
+ [Int] IBeamswapV3PoolImmutables
- [Ext] factory
- [Ext] token0
- [Ext] token1
- [Ext] fee
- [Ext] tickSpacing
- [Ext] maxLiquidityPerTick
+ [Int] IBeamswapV3PoolState
- [Ext] slot0
- [Ext] feeGrowthGlobal0X128
- [Ext] feeGrowthGlobal1X128
- [Ext] protocolFees
- [Ext] liquidity
- [Ext] ticks
- [Ext] tickBitmap
- [Ext] positions
- [Ext] observations
+ [Int] IBeamswapV3PoolDerivedState
- [Ext] observe
- [Ext] snapshotCumulativesInside
+ [Int] IBeamswapV3PoolActions
- [Ext] initialize #
- [Ext] mint #
- [Ext] collect #
- [Ext] burn #
- [Ext] swap #
- [Ext] flash #
- [Ext] increaseObservationCardinalityNext #
+ [Int] IBeamswapV3PoolOwnerActions
- [Ext] setFeeProtocol #
- [Ext] collectProtocol #
- [Ext] setLmPool #
+ [Int] IBeamswapV3PoolEvents
+ [Int] IBeamswapV3Pool (IBeamswapV3PoolImmutables, IBeamswapV3PoolState, IBeamswapV3PoolDerivedState, IBeamswapV3PoolActions, IBeamswapV3PoolOwnerActions, IBeamswapV3PoolEvents)
+ [Lib] LowGasSafeMath
- [Int] add
- [Int] sub
- [Int] mul
- [Int] add
- [Int] sub
+ [Lib] SafeCast
- [Int] toUint160
- [Int] toInt128
- [Int] toInt256
+ [Lib] TickMath
- [Int] getSqrtRatioAtTick
- [Int] getTickAtSqrtRatio
+ [Lib] LiquidityMath
- [Int] addDelta
+ [Lib] Tick
- [Int] tickSpacingToMaxLiquidityPerTick
- [Int] getFeeGrowthInside
- [Int] update #
- [Int] clear #
- [Int] cross #
+ [Lib] BitMath
- [Int] mostSignificantBit
- [Int] leastSignificantBit
+ [Lib] TickBitmap
- [Prv] position
- [Int] flipTick #
- [Int] nextInitializedTickWithinOneWord
+ [Lib] FullMath
- [Int] mulDiv
- [Int] mulDivRoundingUp
+ [Lib] FixedPoint128
+ [Lib] Position
- [Int] get
- [Int] update #
+ [Lib] Oracle
- [Prv] transform
- [Int] initialize #
- [Int] write #
- [Int] grow #
- [Prv] lte
- [Prv] binarySearch
- [Prv] getSurroundingObservations
- [Int] observeSingle
- [Int] observe
+ [Lib] TransferHelper
- [Int] safeApprove #
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeTransferETH #
+ [Lib] UnsafeMath
- [Int] divRoundingUp
+ [Lib] FixedPoint96
+ [Lib] SqrtPriceMath
- [Int] getNextSqrtPriceFromAmount0RoundingUp
- [Int] getNextSqrtPriceFromAmount1RoundingDown
- [Int] getNextSqrtPriceFromInput
- [Int] getNextSqrtPriceFromOutput
- [Int] getAmount0Delta
- [Int] getAmount1Delta
- [Int] getAmount0Delta
- [Int] getAmount1Delta
+ [Lib] SwapMath
- [Int] computeSwapStep
+ [Int] IERC20Minimal
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IBeamswapV3MintCallback
- [Ext] beamswapV3MintCallback #
+ [Int] IBeamswapV3SwapCallback
- [Ext] beamswapV3SwapCallback #
+ [Int] IBeamswapV3FlashCallback
- [Ext] beamswapV3FlashCallback #
+ [Int] IBeamswapV3LmPool
- [Ext] accumulateReward #
- [Ext] crossLmTick #
+ BeamswapV3Pool (IBeamswapV3Pool, NoDelegateCall)
- [Pub] #
- [Prv] checkTicks
- [Int] _blockTimestamp
- [Prv] balance0
- [Prv] balance1
- [Ext] snapshotCumulativesInside
- modifiers: noDelegateCall
- [Ext] observe
- modifiers: noDelegateCall
- [Ext] increaseObservationCardinalityNext #
- modifiers: lock,noDelegateCall
- [Ext] initialize #
- [Prv] _modifyPosition #
- modifiers: noDelegateCall
- [Prv] _updatePosition #
- [Ext] mint #
- modifiers: lock
- [Ext] collect #
- modifiers: lock
- [Ext] burn #
- modifiers: lock
- [Ext] swap #
- modifiers: noDelegateCall
- [Ext] flash #
- modifiers: lock,noDelegateCall
- [Ext] setFeeProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] collectProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] setLmPool #
BeamswapV3Factory Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Int] IBeamswapV3PoolDeployer
- [Ext] parameters
- [Ext] deploy #
+ [Int] IBeamswapV3Factory
- [Ext] owner
- [Ext] feeAmountTickSpacing
- [Ext] getPool
- [Ext] createPool #
- [Ext] setOwner #
- [Ext] setLmPoolDeployer #
- [Ext] enableFeeAmount #
- [Ext] setLmPool #
+ NoDelegateCall
- [Pub] #
- [Prv] checkNotDelegateCall
+ [Int] IBeamswapV3PoolImmutables
- [Ext] factory
- [Ext] token0
- [Ext] token1
- [Ext] fee
- [Ext] tickSpacing
- [Ext] maxLiquidityPerTick
+ [Int] IBeamswapV3PoolState
- [Ext] slot0
- [Ext] feeGrowthGlobal0X128
- [Ext] feeGrowthGlobal1X128
- [Ext] protocolFees
- [Ext] liquidity
- [Ext] ticks
- [Ext] tickBitmap
- [Ext] positions
- [Ext] observations
+ [Int] IBeamswapV3PoolDerivedState
- [Ext] observe
- [Ext] snapshotCumulativesInside
+ [Int] IBeamswapV3PoolActions
- [Ext] initialize #
- [Ext] mint #
- [Ext] collect #
- [Ext] burn #
- [Ext] swap #
- [Ext] flash #
- [Ext] increaseObservationCardinalityNext #
+ [Int] IBeamswapV3PoolOwnerActions
- [Ext] setFeeProtocol #
- [Ext] collectProtocol #
- [Ext] setLmPool #
+ [Int] IBeamswapV3PoolEvents
+ [Int] IBeamswapV3Pool (IBeamswapV3PoolImmutables, IBeamswapV3PoolState, IBeamswapV3PoolDerivedState, IBeamswapV3PoolActions, IBeamswapV3PoolOwnerActions, IBeamswapV3PoolEvents)
+ [Lib] LowGasSafeMath
- [Int] add
- [Int] sub
- [Int] mul
- [Int] add
- [Int] sub
+ [Lib] SafeCast
- [Int] toUint160
- [Int] toInt128
- [Int] toInt256
+ [Lib] TickMath
- [Int] getSqrtRatioAtTick
- [Int] getTickAtSqrtRatio
+ [Lib] LiquidityMath
- [Int] addDelta
+ [Lib] Tick
- [Int] tickSpacingToMaxLiquidityPerTick
- [Int] getFeeGrowthInside
- [Int] update #
- [Int] clear #
- [Int] cross #
+ [Lib] BitMath
- [Int] mostSignificantBit
- [Int] leastSignificantBit
+ [Lib] TickBitmap
- [Prv] position
- [Int] flipTick #
- [Int] nextInitializedTickWithinOneWord
+ [Lib] FullMath
- [Int] mulDiv
- [Int] mulDivRoundingUp
+ [Lib] FixedPoint128
+ [Lib] Position
- [Int] get
- [Int] update #
+ [Lib] Oracle
- [Prv] transform
- [Int] initialize #
- [Int] write #
- [Int] grow #
- [Prv] lte
- [Prv] binarySearch
- [Prv] getSurroundingObservations
- [Int] observeSingle
- [Int] observe
+ [Lib] TransferHelper
- [Int] safeApprove #
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeTransferETH #
+ [Lib] UnsafeMath
- [Int] divRoundingUp
+ [Lib] FixedPoint96
+ [Lib] SqrtPriceMath
- [Int] getNextSqrtPriceFromAmount0RoundingUp
- [Int] getNextSqrtPriceFromAmount1RoundingDown
- [Int] getNextSqrtPriceFromInput
- [Int] getNextSqrtPriceFromOutput
- [Int] getAmount0Delta
- [Int] getAmount1Delta
- [Int] getAmount0Delta
- [Int] getAmount1Delta
+ [Lib] SwapMath
- [Int] computeSwapStep
+ [Int] IERC20Minimal
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IBeamswapV3MintCallback
- [Ext] beamswapV3MintCallback #
+ [Int] IBeamswapV3SwapCallback
- [Ext] beamswapV3SwapCallback #
+ [Int] IBeamswapV3FlashCallback
- [Ext] beamswapV3FlashCallback #
+ [Int] IBeamswapV3LmPool
- [Ext] accumulateReward #
- [Ext] crossLmTick #
+ BeamswapV3Pool (IBeamswapV3Pool, NoDelegateCall)
- [Pub] #
- [Prv] checkTicks
- [Int] _blockTimestamp
- [Prv] balance0
- [Prv] balance1
- [Ext] snapshotCumulativesInside
- modifiers: noDelegateCall
- [Ext] observe
- modifiers: noDelegateCall
- [Ext] increaseObservationCardinalityNext #
- modifiers: lock,noDelegateCall
- [Ext] initialize #
- [Prv] _modifyPosition #
- modifiers: noDelegateCall
- [Prv] _updatePosition #
- [Ext] mint #
- modifiers: lock
- [Ext] collect #
- modifiers: lock
- [Ext] burn #
- modifiers: lock
- [Ext] swap #
- modifiers: noDelegateCall
- [Ext] flash #
- modifiers: lock,noDelegateCall
- [Ext] setFeeProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] collectProtocol #
- modifiers: lock,onlyFactoryOwner
- [Ext] setLmPool #
+ BeamswapV3PoolDeployer (IBeamswapV3PoolDeployer)
- [Pub] #
- [Ext] setFactoryAddress #
- [Ext] deploy #
About SourceHat
SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ 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!
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 SourceHat Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat 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.