AstroBirdsV2 Dividend - Smart Contract Audit Report
Audit Summary
Passive Income is building a new dividend paying token with liquidity adds and a buyback system for burning supply, found here.
For this audit, we reviewed the project team's implementation contract for AstroBirdsV2 at 0x6f1e5bf9420f355e3f12c65d4382be2f9219d3da, Proxy contract at 0x9624cd2e91504692e120802e80a313f84847dc40 and AstroBirdzDividendTracker implementation contract at 0x53789db481ce032b530b0a20de940c21f5a8bd01 on the team's Binance Smart Chain mainnet.
We previously reviewed the project team's launchpad protocol here.
Audit Findings
Please ensure trust in the team prior to investing as they have substantial control in the ecosystem and can upgrade the token contract at any time.
Date: January 24th, 2022.Finding #1 - AstroBirdsV2 - Low
Description: The owner can use the lock() function in order to temporarily set ownership to address(0). Ownership is restored after the duration of time determined by the owner has passed and they use the unlock() function.Risk/Impact: The unlock function has the potential to be used after ownership has been set to address(0), which will restore ownership to the original owner that initially created the ownership lock. This can be used in a nefarious way by the project team to restore ownership and change fee structures.function lock(uint256 time) public virtual onlyOwner { _previousOwner = _owner; _owner = address(0); _lockTime = block.timestamp + time; emit OwnershipTransferred(_owner, address(0)); } function unlock() public virtual { require( _previousOwner == msg.sender, "You don't have permission to unlock" ); emit OwnershipTransferred(_owner, _previousOwner); _owner = _previousOwner; }
Recommendation: We recommend upgrading the contract to a fixed version in which the unlock function be modified to set the previous owner variable equal to address(0) at the end of the unlock function to prevent it from being used more than once per lock.Finding #2 - AstroBirdsV2 - Low
Description: The current dividend amount is calculated based on the set percentage of the dividend fee amount from the total fee amount in the contract.
Risk/Impact: Since the AstroBirdsV2 contract swaps the BNB collected from dividend fees for $PSI tokens to be distributed by the AstroBirdzDividendTracker contract, it is possible that the dividend amount can cause the $PSI token to be susceptible to frontrunning.
Recommendation: The project team should ensure that the dividend amount is not too large to mitigate the risk of frontrunning as well as monitor for suspicious acivity.
Contracts Overview
AstroBirdsV2 Contract:AstroBirdzDividendTracker Contract:
- The total supply of the token is currently 413.4 million $ABZ [413,437,361].
- The team has the ability to mint any amount of tokens to any address at any time.
- In addition to the burn function, tokens can also be sent to the 0x..dead address to reduce the circulating supply at any time.
- At the time of writing this report, 66.6% of the total supply is stored in the contract and 7.25% has been sent to the dead address.
- The next five holders own a cumulative 7.3% of the total supply.
- Currently, 0.87% of the token's supply is in PancakeSwap liquidity.
- Of that liquidity, 91.8% of the LP tokens are stored in a PSI token locking contract
- The deployer address holds 8% of the LP tokens.
- There is a maximum transaction amount, but this does not apply during transactions where the owner is either the sender or the recipient.
- There is a maximum sell limit when the sell limiter is enabled.
- There is a liquidity, marketing, PSI, buyback and team fee on all transactions for any non-excluded address that participates in a transfer. All fees have a maximum limit of 15% each for a maximum total of 75%
- The fees charged on transactions are stored in the contract and, once a threshold value is met, are swapped for BNB and used to fund PancakeSwap liquidity.
- Liquidity-adds are funded by selling half of the tokens collected as liquidity fees, pairing the received BNB with the token, and adding it as liquidity to the BNB pair.
- The recipient of the newly created LP tokens is the owner's address. We recommend that the team locks these newly acquired LP tokens.
- Of the remaining tokens, 1% of the BNB is automatically swapped for $PSI and distributed as dividends to the AstrobirdzDividendTracker contract, 3% is transferred to the marketing wallet controlled by the team, 1% is transferred to the team wallet, and the remaining part is sent to the buyback address also controlled by the team.
- The owner can mint any amount of tokens to any address at any time unless the owner address is a contract address.
- The owner can set the buyback address, marketing address, team address, and PSI address at any time.
- The owner must initialize the AstroBirdzDividendTracker contract.
- The owner can individually set the liquidity, marketing, PSI, and buyback fees to any value up to 15% at any time.
- The owner can mint any amount of tokens to any address at any time unless the owner address is a contract address.
- The owner can set a maximum transfer amount at any time.
- The owner can include and exclude accounts from transfer fees and dividends at any time.
- The owner can toggle and set the sell limit to any value at any time.
- The owner can toggle buying and selling tokens at any time.
- The owner can toggle transfer functionality at any time.
- The owner is able to update the maximum amount of gas used for processing to a value between 200,000 and 500,000 at any time; the initial value is 300,000.
- As the contract is deployed with Solidity v0.8.10, it is protected from overflow/underflow attacks along with following the ERC20 standard.
- The contract is upgradable, meaning the team can swap out the current contract for a new one at any time.
- A user must hold 100,000 $ABZ tokens (0.02% of the total supply) to be eligible for dividends
- Once dividends are distributed, they will need to be claimed; claiming happens automatically on each transfer.
- Dividend rewards can also be claimed manually by kicking off the claim cycle, which will process all eligible token holders.
- There is a wait-time of 3600 seconds (1 hour) between claiming dividend rewards.
- Claimed dividends are sent to the user's wallet address.
- The owner can update the minimum $ABZ token amount to be eligible for dividends at any time.
- The owner can set the balance of any user to any value at any time.
- The owner is able to update the amount of time a user must wait between claiming dividends to a value between 1 and 24 hours (in seconds).
- The owner is able to exclude any address from dividends at any time.
External Threat Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Centralization of Control | WARNING | |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Deprecated Opcodes | N/A | PASS |
Ether Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | 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 |
AstroBirdsV2 Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Initializable
+ ContextUpgradeable (Initializable)
- [Int] __Context_init #
- modifiers: initializer
- [Int] __Context_init_unchained #
- modifiers: initializer
- [Int] _msgSender
- [Int] _msgData
+ [Lib] AddressUpgradeable
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] verifyCallResult
+ [Int] IERC20Upgradeable
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IPancakeFactory
- [Ext] feeTo
- [Ext] feeToSetter
- [Ext] getPair
- [Ext] allPairs
- [Ext] allPairsLength
- [Ext] createPair #
- [Ext] setFeeTo #
- [Ext] setFeeToSetter #
+ [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] 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] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IERC20Metadata (IERC20)
- [Ext] name
- [Ext] symbol
- [Ext] decimals
+ [Int] IBEP20 (IERC20, IERC20Metadata)
- [Ext] getOwner
+ [Int] IDividendPayingTokenInterface
- [Ext] dividendToken
- [Ext] parentToken
- [Ext] totalDividendsDistributed
- [Ext] dividendOf
- [Ext] withdrawDividend #
- [Ext] distributeDividends #
+ [Int] IDividendPayingTokenOptionalInterface
- [Ext] withdrawableDividendOf
- [Ext] withdrawnDividendOf
- [Ext] accumulativeDividendOf
+ [Int] IERC20TokenRecover
- [Ext] recoverERC20 #
- [Ext] recoverETH #
+ [Int] IDividendTracker (IBEP20, IDividendPayingTokenInterface, IDividendPayingTokenOptionalInterface, IERC20TokenRecover)
- [Ext] lastProcessedIndex
- [Ext] excludedFromDividends
- [Ext] lastClaimTimes
- [Ext] claimWait
- [Ext] minimumTokenBalanceForDividends
- [Ext] excludeFromDividends #
- [Ext] includeInDividends #
- [Ext] updateClaimWait #
- [Ext] updateMinTokenBalance #
- [Ext] getLastProcessedIndex
- [Ext] getNumberOfTokenHolders
- [Ext] getAccount
- [Ext] getAccountAtIndex
- [Ext] ensureBalance #
- [Ext] ensureBalanceForUsers #
- [Ext] ensureBalanceForUser #
- [Ext] setBalance #
- [Ext] process #
- [Ext] processAccount #
+ ERC20Upgradeable (Initializable, ContextUpgradeable, IERC20Upgradeable)
- [Ext] ($)
- [Int] _ERC20_init #
- modifiers: initializer
- [Ext] initPSIDividendTracker #
- modifiers: onlyOwner
- [Pub] name
- [Pub] symbol
- [Pub] decimals
- [Pub] totalSupply
- [Pub] balanceOf
- [Int] calculateLiquidityFee
- [Int] calculatePSIFee
- [Int] calculateMarketingFee
- [Int] calculateTeamFee
- [Int] calculateBuybackFee
- [Pub] setPSIFee #
- modifiers: onlyOwner
- [Pub] setLiquidityFee #
- modifiers: onlyOwner
- [Pub] setBuybackFee #
- modifiers: onlyOwner
- [Pub] setMarketingFee #
- modifiers: onlyOwner
- [Pub] setTeamFee #
- modifiers: onlyOwner
- [Ext] toggleSellLimit #
- modifiers: onlyOwner
- [Pub] setBuybackAddress #
- modifiers: onlyOwner
- [Pub] changeMarketingAddress #
- modifiers: onlyOwner
- [Pub] changeTeamAddress #
- modifiers: onlyOwner
- [Pub] changePSIAddress #
- modifiers: onlyOwner
- [Pub] changeLiquidityAddress #
- modifiers: onlyOwner
- [Pub] changeSellLimit #
- modifiers: onlyOwner
- [Pub] changeMaxtx #
- modifiers: onlyOwner
- [Pub] addExcludedAddress #
- modifiers: onlyOwner
- [Pub] removeExcludedAddress #
- modifiers: onlyOwner
- [Pub] excludeFromFeesAndDividends #
- modifiers: onlyOwner
- [Ext] addNewRouter #
- modifiers: onlyOwner
- [Ext] setAutomatedMarketMakerPair #
- modifiers: onlyOwner
- [Prv] _setAutomatedMarketMakerPair #
- [Ext] updateGasForProcessing #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Pub] getUnlockTime
- [Pub] lock #
- modifiers: onlyOwner
- [Pub] unlock #
- [Pub] multiTransfer #
- [Ext] processDividendTracker #
- [Pub] transfer #
- [Pub] allowance
- [Pub] approve #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Pub] setSwapAndLiquifyEnabled #
- modifiers: onlyOwner
- [Int] _transferExcluded #
- [Int] _transfer #
- [Prv] _fixDividendTrackerBalancer #
- [Int] _simpleTransfer #
- [Ext] performSwapAndLiquify #
- modifiers: onlyOwner
- [Prv] swapAndLiquify #
- modifiers: lockTheSwap
- [Pub] toggleTrading #
- modifiers: onlyOwner
- [Pub] togglePaused #
- modifiers: onlyOwner
- [Prv] swapTokensForEth #
- [Prv] addLiquidity #
- [Prv] swapAndSendDividends #
- [Prv] swapETHForPSI #
- [Int] _mint #
- [Ext] mint #
- modifiers: onlyOwner
- [Int] _burn #
- [Ext] burn #
- [Int] _approve #
- [Int] _setupDecimals #
- [Int] _beforeTokenTransfer #
+ AstroBirdsV2 (Initializable, ERC20Upgradeable)
- [Pub] initialize #
- modifiers: initializer
AstroBirdzDividendTracker Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Ownable (Context)
- [Pub] #
- [Pub] owner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ [Lib] IterableMapping
- [Int] get
- [Int] getIndexOfKey
- [Int] getKeyAtIndex
- [Int] size
- [Int] set #
- [Int] remove #
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
+ [Int] IERC20Metadata (IERC20)
- [Ext] name
- [Ext] symbol
- [Ext] decimals
+ ERC20 (Context, IERC20, IERC20Metadata)
- [Pub] #
- [Pub] name
- [Pub] symbol
- [Pub] decimals
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] transfer #
- [Pub] allowance
- [Pub] approve #
- [Pub] transferFrom #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
- [Int] _transfer #
- [Int] _mint #
- [Int] _burn #
- [Int] _approve #
- [Int] _beforeTokenTransfer #
- [Int] _afterTokenTransfer #
+ [Int] IDividendPayingTokenInterface
- [Ext] dividendToken
- [Ext] parentToken
- [Ext] totalDividendsDistributed
- [Ext] dividendOf
- [Ext] withdrawDividend #
- [Ext] distributeDividends #
+ [Int] IDividendPayingTokenOptionalInterface
- [Ext] withdrawableDividendOf
- [Ext] withdrawnDividendOf
- [Ext] accumulativeDividendOf
+ DividendPayingToken (Ownable, ERC20, IDividendPayingTokenInterface, IDividendPayingTokenOptionalInterface)
- [Pub] #
- modifiers: ERC20
- [Pub] distributeDividends #
- modifiers: onlyOwnerOrParentToken
- [Pub] withdrawDividend #
- [Int] _withdrawDividendOfUser #
- [Pub] dividendOf
- [Pub] withdrawableDividendOf
- [Pub] withdrawnDividendOf
- [Pub] accumulativeDividendOf
- [Int] _transfer #
- [Int] _mint #
- [Int] _burn #
- [Int] _setBalance #
+ [Lib] Address
- [Int] isContract
- [Int] sendValue #
- [Int] functionCall #
- [Int] functionCall #
- [Int] functionCallWithValue #
- [Int] functionCallWithValue #
- [Int] functionStaticCall
- [Int] functionStaticCall
- [Int] functionDelegateCall #
- [Int] functionDelegateCall #
- [Int] verifyCallResult
+ [Lib] SafeERC20
- [Int] safeTransfer #
- [Int] safeTransferFrom #
- [Int] safeApprove #
- [Int] safeIncreaseAllowance #
- [Int] safeDecreaseAllowance #
- [Prv] _callOptionalReturn #
+ [Int] IERC20TokenRecover
- [Ext] recoverERC20 #
- [Ext] recoverETH #
+ ERC20TokenRecover (Ownable, IERC20TokenRecover)
- [Pub] recoverERC20 #
- modifiers: onlyOwner
- [Pub] recoverETH #
- modifiers: onlyOwner
+ [Int] IBEP20 (IERC20, IERC20Metadata)
- [Ext] getOwner
+ [Int] IDividendTracker (IBEP20, IDividendPayingTokenInterface, IDividendPayingTokenOptionalInterface, IERC20TokenRecover)
- [Ext] lastProcessedIndex
- [Ext] excludedFromDividends
- [Ext] lastClaimTimes
- [Ext] claimWait
- [Ext] minimumTokenBalanceForDividends
- [Ext] excludeFromDividends #
- [Ext] includeInDividends #
- [Ext] updateClaimWait #
- [Ext] updateMinTokenBalance #
- [Ext] getLastProcessedIndex
- [Ext] getNumberOfTokenHolders
- [Ext] getAccount
- [Ext] getAccountAtIndex
- [Ext] ensureBalance #
- [Ext] ensureBalanceForUsers #
- [Ext] ensureBalanceForUser #
- [Ext] setBalance #
- [Ext] process #
- [Ext] processAccount #
+ AstroBirdzDividendTracker (Ownable, DividendPayingToken, ERC20TokenRecover, IDividendTracker)
- [Pub] #
- modifiers: DividendPayingToken
- [Pub] getOwner
- [Pub] recoverERC20 #
- modifiers: onlyOwnerOrParentToken
- [Int] _transfer
- [Pub] withdrawDividend
- [Ext] excludeFromDividends #
- modifiers: onlyOwnerOrParentToken
- [Ext] includeInDividends #
- modifiers: onlyOwnerOrParentToken
- [Ext] updateClaimWait #
- modifiers: onlyOwnerOrParentToken
- [Ext] updateMinTokenBalance #
- modifiers: onlyOwnerOrParentToken
- [Ext] getLastProcessedIndex
- [Ext] getNumberOfTokenHolders
- [Pub] getAccount
- [Ext] getAccountAtIndex
- [Prv] canAutoClaim
- [Ext] ensureBalance #
- [Ext] ensureBalanceForUsers #
- modifiers: onlyOwnerOrParentToken
- [Prv] bytesToAddress
- [Pub] ensureBalanceForUser #
- modifiers: onlyOwnerOrParentToken
- [Ext] setBalance #
- modifiers: onlyOwnerOrParentToken
- [Ext] process #
- [Pub] processAccount #
- modifiers: onlyOwnerOrParentToken