PaintSwap - Smart Contract Audit Report
Summary
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.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:PaintFactory 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.
PaintRouter 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.
- 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 Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Deprecated Opcodes | N/A | PASS |
Ether/Token Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Multiple Sends | N/A | PASS |
Suicide | N/A | PASS |
State Change External Calls | N/A | PASS |
Unchecked Retval | N/A | PASS |
User Supplied Assertion | N/A | PASS |
Critical Solidity Compiler | N/A | PASS |
Overall Contract Safety | PASS |
Details: Brush Token
($) = 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
($) = 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
($) = 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
($) = 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
($) = 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
($) = 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