PaintSwap - Smart Contract Audit Report

Summary

PaintSwap Audit Report PaintSwap is building a new decentralized exchange with a yield farming platform on the Fantom Network.

For this audit, we analyzed the PaintSwap Token, Decorator, Timelock, Factory, Router, and Multicall contracts on the Fantom Network mainnet.

  • Token - 0x85dec8c4B2680793661bCA91a8F129607571863d
  • Decorator - 0xcb80f529724b9620145230a0c866ac2facbe4e3d
  • TimeLock - 0xf3385E00E84FB89DfB7586e0b8BD858F893E0dDB
  • Factory - 0x733A9D1585f2d14c77b49d39BC7d7dd14CdA4aa5
  • Router - 0xfD000ddCEa75a2E23059881c3589F6425bFf1AbB
  • Multicall - 0x11473D6E641dF17cd6331D45b135E35B49edBea8
  • Notes on Individual Contracts:

    Token Contract:
  • The total supply of $BRUSH Token is currently 175.7 million [175,668,772].
  • The owner has the ability to mint any amount of tokens to any address at any time.
  • Any user can burn their own tokens to reduce the total supply.
  • At the time of writing this report, 26.73% of the total token supply is stored in the Decorator contract.
  • 10.87% of the total token supply is stored in the ArtGallery contract. This contract was out of scope for the purpose of this audit.
  • 9.41% of the total token supply is in liquidity.
  • Of that liquidity, 97.69% of the LP tokens are staked in the Decorator contract.
  • The top five holders own a cumulative 28.87% of the total supply.

  • The owner of the PaintSwap token contract has been properly set as the Decorator contract.
  • The $BRUSH token is designed to be a governance token where 1 token = 1 vote.
  • Token holders can delegate their voting rights to any address. To save gas, users can also do so using an EIP-712 signature.
  • There are no fees associated with transferring tokens.
  • Utilization of SafeMath (or similarily safe functions) to prevent overflows.
  • The contract complies with the ERC20 Token Standard.

  • Decorator Contract:
    • Users can stake various tokens into the Decorator contract to earn rewards in the form of the project's native $BRUSH token.
    • There is no fee associated with making a deposit to the contract.
    • The owner can pause/unpause the ability to deposit into the contract at any time.
    • Pending rewards are automatically distributed to users upon each deposit or withdraw.
    • Half of the pending rewards are sent to the ArtGallery address and are intended to be locked for a period of time. This functionality cannot be confirmed as the ArtGallery contract was out of scope for the audit. The remaining half of the pending rewards are sent to the user.
    • There is a fee associated with making a withdrawal from the contract. If a user is withdrawing from the $BRUSH token pool, the tokens collected from the withdraw fee are burned. If a user is withdrawing from any other pools, any $BRUSH tokens are burnt while the rest of the tokens are used to buy-back $BRUSH and subsequently burn the tokens.
    • The owner can update the withdraw fee to any value at any time.
    • The owner can set the emissions rate of the Decorator contract to any value at any time.
    • An emergency withdraw function is present, allowing users to withdraw their tokens in case of an issue, but that user's rewards will be forfeited and the withdraw fee will be charged.
    • The Decorator staking contract should not be used with deflationary, fee-on-transfer, or ERC-777 tokens. If a fee-on-transfer token is added as a staking asset, then the contract must be exempt from transfer fees. The use of a fee-on-transfer token will cause balances to be inaccurate as the tokens collected from fees are not accounted for.
    • The owner of the Decorator contract is designed to be the Timelock contract, which is designed to delay the execution of transactions. The Timelock default minimum delay is 24 hours.
    • The contract utilizes SafeMath (or similarily safe functions) to prevent overflows/underflows.
    PaintFactory Contract:
    • The PaintFactory contract is responsible for the creation of liquidity pairs for the token, thereby enabling trading on the platform.
    • The initialize() function is called from the Pair contract which allows the factory to specify the two ERC20 tokens that this pair will exchange.
    • Once the pool is created, its address is stored with a double mapping that takes both token addresses as input.
    PaintRouter Contract:
    • The PaintRouter contract is used to interact with the liquidity pool that was created in the PaintFactory contract.
    • PaintRouter routes orders to the user-determined pair contract to swap assets.
    • This contract performs requirement checks needed for swapping tokens, adding liquidity, and removing liquidity.

    Audit Findings Summary:
    • No threats from external attackers were identified.
    • Ensure trust in the team as they have substantial control within the ecosystem.
    • The owner of the Decorator contract is the Timelock contract, which is designed to delay the execution of transactions. The Timelock default minimum delay is 24 hours.
    • Date: November 1st, 2021

    Combined External Threat Results

    Vulnerability CategoryNotesResult
    Arbitrary Storage WriteN/APASS
    Arbitrary JumpN/APASS
    Delegate Call to Untrusted ContractN/APASS
    Dependence on Predictable VariablesN/APASS
    Deprecated OpcodesN/APASS
    Ether/Token ThiefN/APASS
    ExceptionsN/APASS
    External CallsN/APASS
    Integer Over/UnderflowN/APASS
    Multiple SendsN/APASS
    SuicideN/APASS
    State Change External CallsN/APASS
    Unchecked RetvalN/APASS
    User Supplied AssertionN/APASS
    Critical Solidity CompilerN/APASS
    Overall Contract Safety PASS

    Details: Brush Token

    Smart Contract Graph

    Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
    
     + [Int] IBEP20 
        - [Ext] totalSupply
        - [Ext] decimals
        - [Ext] symbol
        - [Ext] name
        - [Ext] getOwner
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     +  BEP20 (Context, IBEP20, Ownable)
        - [Pub]  #
        - [Ext] getOwner
        - [Pub] name
        - [Pub] symbol
        - [Pub] decimals
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Pub] transfer #
        - [Pub] allowance
        - [Pub] approve #
        - [Pub] transferFrom #
        - [Pub] increaseAllowance #
        - [Pub] decreaseAllowance #
        - [Pub] mint #
           - modifiers: onlyOwner
        - [Int] _transfer #
        - [Int] _mint #
        - [Int] _burn #
        - [Int] _approve #
        - [Int] _burnFrom #
    
     +  BrushToken (BEP20)
        - [Pub] mint #
           - modifiers: onlyOwner
        - [Pub] burn #
           - modifiers: onlyOwner
        - [Ext] burn #
        - [Ext] delegates
        - [Ext] delegate #
        - [Ext] delegateBySig #
        - [Ext] getCurrentVotes
        - [Ext] getPriorVotes
        - [Int] _delegate #
        - [Int] _moveDelegates #
        - [Int] _writeCheckpoint #
        - [Int] safe32
    

    Details: Decorator

    Smart Contract Graph

    Smart Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     + [Int] IBEP20 
        - [Ext] totalSupply
        - [Ext] decimals
        - [Ext] symbol
        - [Ext] name
        - [Ext] getOwner
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     + [Int] IPancakePair 
        - [Ext] MINIMUM_LIQUIDITY
        - [Ext] factory
        - [Ext] token0
        - [Ext] token1
        - [Ext] getReserves
        - [Ext] price0CumulativeLast
        - [Ext] price1CumulativeLast
        - [Ext] kLast
        - [Ext] mint #
        - [Ext] burn #
        - [Ext] swap #
        - [Ext] skim #
        - [Ext] sync #
        - [Ext] initialize #
    
     + [Int] IArtGallery 
        - [Ext] lock #
    
     + [Int] IPancakeRouter01 
        - [Ext] factory
        - [Ext] WETH
        - [Ext] addLiquidity #
        - [Ext] addLiquidityETH ($)
        - [Ext] removeLiquidity #
        - [Ext] removeLiquidityETH #
        - [Ext] removeLiquidityWithPermit #
        - [Ext] removeLiquidityETHWithPermit #
        - [Ext] swapExactTokensForTokens #
        - [Ext] swapTokensForExactTokens #
        - [Ext] swapExactETHForTokens ($)
        - [Ext] swapTokensForExactETH #
        - [Ext] swapExactTokensForETH #
        - [Ext] swapETHForExactTokens ($)
        - [Ext] quote
        - [Ext] getAmountOut
        - [Ext] getAmountIn
        - [Ext] getAmountsOut
        - [Ext] getAmountsIn
    
     + [Int] IPancakeRouter02 (IPancakeRouter01)
        - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
        - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
        - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
        - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
        - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
    
     + [Lib] Address 
        - [Int] isContract
        - [Int] sendValue #
        - [Int] functionCall #
        - [Int] functionCall #
        - [Int] functionCallWithValue #
        - [Int] functionCallWithValue #
        - [Int] functionStaticCall
        - [Int] functionStaticCall
        - [Prv] _verifyCallResult
    
     + [Lib] SafeBEP20 
        - [Int] safeTransfer #
        - [Int] safeTransferFrom #
        - [Int] safeApprove #
        - [Int] safeIncreaseAllowance #
        - [Int] safeDecreaseAllowance #
        - [Prv] _callOptionalReturn #
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
    
     +  BEP20 (Context, IBEP20, Ownable)
        - [Pub]  #
        - [Ext] getOwner
        - [Pub] name
        - [Pub] symbol
        - [Pub] decimals
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Pub] transfer #
        - [Pub] allowance
        - [Pub] approve #
        - [Pub] transferFrom #
        - [Pub] increaseAllowance #
        - [Pub] decreaseAllowance #
        - [Pub] mint #
           - modifiers: onlyOwner
        - [Int] _transfer #
        - [Int] _mint #
        - [Int] _burn #
        - [Int] _approve #
        - [Int] _burnFrom #
    
     +  BrushToken (BEP20)
        - [Pub] mint #
           - modifiers: onlyOwner
        - [Pub] burn #
           - modifiers: onlyOwner
        - [Ext] burn #
        - [Ext] delegates
        - [Ext] delegate #
        - [Ext] delegateBySig #
        - [Ext] getCurrentVotes
        - [Ext] getPriorVotes
        - [Int] _delegate #
        - [Int] _moveDelegates #
        - [Int] _writeCheckpoint #
        - [Int] safe32
    
     +  Decorator (Ownable)
        - [Pub]  #
        - [Ext] poolLength
        - [Pub] poolExists
        - [Pub] add #
           - modifiers: onlyOwner
        - [Pub] set #
           - modifiers: onlyOwner
        - [Int] getMultiplier
        - [Ext] pendingBrush
        - [Pub] massUpdatePools #
        - [Pub] updatePool #
        - [Prv] distributePendingRewards #
        - [Int] universalApprove #
        - [Pub] buyBackAndBurn #
        - [Pub] buyBackAndBurnAll #
        - [Pub] deposit #
        - [Pub] withdraw #
        - [Pub] enterStaking #
        - [Pub] leaveStaking #
        - [Int] _withdraw #
        - [Pub] emergencyWithdraw #
        - [Int] safeBrushTransfer #
        - [Pub] setBrushPerSecondEmissionRate #
           - modifiers: onlyOwner
        - [Pub] setStartTime #
           - modifiers: onlyOwner
        - [Pub] setDepositsDisabled #
           - modifiers: onlyOwner
        - [Pub] setInverseWithdrawFeeLP #
           - modifiers: onlyOwner
        - [Pub] setInverseWithdrawFeeSingle #
           - modifiers: onlyOwner
    

    Details: Timelock

    Smart Contract Graph

    Smart Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     +  Timelock 
        - [Pub]  #
        - [Ext]  ($)
        - [Pub] setDelay #
        - [Pub] acceptAdmin #
        - [Pub] setPendingAdmin #
        - [Pub] queueTransaction #
        - [Pub] cancelTransaction #
        - [Pub] executeTransaction ($)
        - [Int] getBlockTimestamp
    
    

    Details: Router

    Smart Contract Graph

    Smart Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
    
     + [Int] IERC20 
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transfer #
        - [Ext] transferFrom #
    
     + [Int] IPancakeFactory 
        - [Ext] feeTo
        - [Ext] feeToSetter
        - [Ext] getPair
        - [Ext] allPairs
        - [Ext] allPairsLength
        - [Ext] createPair #
        - [Ext] setFeeTo #
        - [Ext] setFeeToSetter #
        - [Ext] locked
        - [Ext] setLocked #
    
     + [Int] IPancakeRouter01 
        - [Ext] factory
        - [Ext] WETH
        - [Ext] addLiquidity #
        - [Ext] addLiquidityETH ($)
        - [Ext] removeLiquidity #
        - [Ext] removeLiquidityETH #
        - [Ext] removeLiquidityWithPermit #
        - [Ext] removeLiquidityETHWithPermit #
        - [Ext] swapExactTokensForTokens #
        - [Ext] swapTokensForExactTokens #
        - [Ext] swapExactETHForTokens ($)
        - [Ext] swapTokensForExactETH #
        - [Ext] swapExactTokensForETH #
        - [Ext] swapETHForExactTokens ($)
        - [Ext] quote
        - [Ext] getAmountOut
        - [Ext] getAmountIn
        - [Ext] getAmountsOut
        - [Ext] getAmountsIn
    
     + [Int] IPancakeRouter02 (IPancakeRouter01)
        - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
        - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
        - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
        - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
        - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
    
     + [Int] IPaintPair 
        - [Ext] getReserves
    
     + [Lib] PaintLibrary 
        - [Int] sortTokens
        - [Int] pairFor
        - [Int] getReserves
        - [Int] quote
        - [Int] getAmountOut
        - [Int] getAmountIn
        - [Int] getAmountsOut
        - [Int] getAmountsIn
    
     + [Lib] TransferHelper 
        - [Int] safeApprove #
        - [Int] safeTransfer #
        - [Int] safeTransferFrom #
        - [Int] safeTransferETH #
    
     + [Int] IPancakePair 
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transfer #
        - [Ext] transferFrom #
        - [Ext] DOMAIN_SEPARATOR
        - [Ext] PERMIT_TYPEHASH
        - [Ext] nonces
        - [Ext] permit #
        - [Ext] MINIMUM_LIQUIDITY
        - [Ext] factory
        - [Ext] token0
        - [Ext] token1
        - [Ext] getReserves
        - [Ext] price0CumulativeLast
        - [Ext] price1CumulativeLast
        - [Ext] kLast
        - [Ext] mint #
        - [Ext] burn #
        - [Ext] swap #
        - [Ext] skim #
        - [Ext] sync #
        - [Ext] initialize #
    
     + [Int] IWETH 
        - [Ext] deposit ($)
        - [Ext] transfer #
        - [Ext] withdraw #
    
     + [Int] BrushToken 
        - [Ext] burn #
    
     +  PaintRouter (IPancakeRouter02)
        - [Pub]  #
        - [Ext]  ($)
        - [Int] _addLiquidity #
        - [Ext] addLiquidity #
           - modifiers: ensure
        - [Ext] addLiquidityETH ($)
           - modifiers: ensure
        - [Pub] removeLiquidity #
           - modifiers: ensure
        - [Pub] removeLiquidityETH #
           - modifiers: ensure
        - [Ext] removeLiquidityWithPermit #
        - [Ext] removeLiquidityETHWithPermit #
        - [Pub] removeLiquidityETHSupportingFeeOnTransferTokens #
           - modifiers: ensure
        - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
        - [Int] _swap #
        - [Pub] swapExactTokensForTokens #
           - modifiers: ensure
        - [Ext] swapTokensForExactTokens #
           - modifiers: ensure
        - [Pub] swapExactETHForTokens ($)
           - modifiers: ensure
        - [Ext] swapTokensForExactETH #
           - modifiers: ensure
        - [Ext] swapExactTokensForETH #
           - modifiers: ensure
        - [Ext] swapETHForExactTokens ($)
           - modifiers: ensure
        - [Int] _swapSupportingFeeOnTransferTokens #
        - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
           - modifiers: ensure
        - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
           - modifiers: ensure
        - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
           - modifiers: ensure
        - [Pub] quote
        - [Pub] getAmountOut
        - [Pub] getAmountIn
        - [Pub] getAmountsOut
        - [Pub] getAmountsIn
    
    

    Details: Factory

    Smart Contract Graph

    Smart Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     + [Int] IPancakeFactory 
        - [Ext] feeTo
        - [Ext] feeToSetter
        - [Ext] getPair
        - [Ext] allPairs
        - [Ext] allPairsLength
        - [Ext] createPair #
        - [Ext] setFeeTo #
        - [Ext] setFeeToSetter #
        - [Ext] locked
        - [Ext] setLocked #
    
     + [Int] IPancakePair 
        - [Ext] MINIMUM_LIQUIDITY
        - [Ext] factory
        - [Ext] token0
        - [Ext] token1
        - [Ext] getReserves
        - [Ext] price0CumulativeLast
        - [Ext] price1CumulativeLast
        - [Ext] kLast
        - [Ext] mint #
        - [Ext] burn #
        - [Ext] swap #
        - [Ext] skim #
        - [Ext] sync #
        - [Ext] initialize #
    
     + [Int] IPancakeERC20 
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transfer #
        - [Ext] transferFrom #
        - [Ext] DOMAIN_SEPARATOR
        - [Ext] PERMIT_TYPEHASH
        - [Ext] nonces
        - [Ext] permit #
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  PancakeERC20 (IPancakeERC20)
        - [Pub]  #
        - [Int] _mint #
        - [Int] _burn #
        - [Prv] _approve #
        - [Prv] _transfer #
        - [Ext] approve #
        - [Ext] transfer #
        - [Ext] transferFrom #
        - [Ext] permit #
    
     + [Lib] Math 
        - [Int] min
        - [Int] sqrt
    
     + [Lib] UQ112x112 
        - [Int] encode
        - [Int] uqdiv
    
     + [Int] IERC20 
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transfer #
        - [Ext] transferFrom #
    
     + [Int] IPancakeCallee 
        - [Ext] pancakeCall #
    
     +  PancakePair (IPancakePair, PancakeERC20)
        - [Pub] getReserves
        - [Prv] _safeTransfer #
        - [Pub]  #
        - [Ext] initialize #
        - [Prv] _update #
        - [Prv] _mintFee #
        - [Ext] mint #
           - modifiers: lock
        - [Ext] burn #
           - modifiers: lock
        - [Ext] swap #
           - modifiers: lock
        - [Ext] skim #
           - modifiers: lock
        - [Ext] sync #
           - modifiers: lock
    
     +  PaintFactory (IPancakeFactory)
        - [Pub]  #
        - [Ext] allPairsLength
        - [Ext] createPair #
        - [Ext] setFeeTo #
        - [Ext] setFeeToSetter #
        - [Ext] setLocked #
    

    Details: PaintMulticall

    Smart Contract Graph

    Smart Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
    
     +  PaintMulticall 
        - [Pub] aggregate #
        - [Pub] getEthBalance
        - [Pub] getBlockHash
        - [Pub] getLastBlockHash
        - [Pub] getCurrentBlockTimestamp
        - [Pub] getCurrentBlockDifficulty
        - [Pub] getCurrentBlockGasLimit
        - [Pub] getCurrentBlockCoinbase