ApeApe.finance Token & Staking - Smart Contract Audit Report

Summary

ApeApe.finance Audit Report The ApeApe.finance is building an innovative Defi platform that reduces supply via a Transaction Deflation Protocol, incentivises transactions and volume with a Transaction Fee Reward Mechanism and motivates holding via a Transaction Fee Staking Reward Contract.

We reviewed ApeApe.finance's contracts on Goerli testnet at the following addresses:
  • Token/Presale - TBD
  • Staking - TBD
  • RewardCollector - TBD
  • Notes on the Contracts:

      Token & Presale Contract:
    • There is a 1% burn fee on transfers of the token.
    • An additional 1% is stored in the contract for the last 20 transfers of the token.
    • 3% of transfers are also sent to the RewardCollector contract to provide rewards for staking. Once every 7 days these rewards are forwarded to the RewardCollector contract.
    • Every 20th transaction results in the rewards from the previous 20 transactions being sent to the sender.
    • The owner and the Staking/RewardCollector contracts are excluded from all transfer fees.

    • The total supply of the token is 100,000.
    • 60,000 tokens are allocated for the project's presale at 1,000 tokens per ETH.
    • Transfers of the token are locked during the presale and setup of the platform.
    • Tokens sold in the presale are deducted from the owner's balance; the owner must maintain enough tokens in his wallet for the presale to occur.
    • At the conclusion of the presale, all of the ETH raised is sent to the owner's address.

    • The owner can update the address of the staking contract and reward collector contract at any time. We advise only allowing this function to be called once by requiring the addresses to be set be equal to address(0) (the default value).
    • Users cannot transfer tokens until these addresses are set.
    • The owner can lock transfers of the token for a 72 hour period.
    • Transfers and rewards are disabled for the first 72 hours.

    • Gas can be saved via streamlining logic in the _transfer() function (realAmount and totalFees are unnecessarily defined) and the functions to end the presale.
    • The presale purchasing function references a 'returnWei' variable, but it is not used in a meaninful manner; serving only to waste gas. The endPresale() function also unnecessarily transfers ETH to the owner, when the contract shoud have no ETH balance.
    • Utilization of SafeMath to prevent overflows.


    • Staking Contract:
    • Users can stake the project's token and recieve a token representing their stake.
    • When users stake their tokens, they are locked up for a minimum of 7 days.
    • 90% of the tokens collected from the 3% transfer tax and sent to the RewardCollector are used every 7 days to provide rewards for users staked in this contract.

    • The owner has the ability to update the rewardCollector address at any time. We advise only allowing this function to be called once by requiring the addresses to be set be equal to address(0) (the default value).
    • The checkStart modifier is applied in some functions even though it us guaranteed to return true; wasting gas.
    • Variables are unnecessarily defined as 0, wasting gas upon deployment. the trueReward variable is also used without good reason.
    • Utilization of SafeMath and SafeERC20 to prevent overflows and ensure safe transfers.


    • RewardCollector Contract:
    • This contract holds the rewards collected from transfer fees and distributes them to the staking pool.
    • Rewards can only be distributed once every at least 7 days.
    • Anyone can call notifyExternal() to distribute the rewards.

    • The contract defines a series of variables (_totalSupply & _balancesrelated) to balances and total supply which are unused. The stakingContract varaible is also not needed and can be replaced by address(stakingInstance).
    • The contract also includes functions that will always return 0.
    Audit Findings Summary:
    • No security issues from outside attackers were identified.
    • Date: February 14th, 2021.
    • Update Date: February 23rd, 2021. Implementation of recommendations from the audit further limiting owner control and optimizing for gas savings.

    Vulnerability CategoryNotesResult
    Arbitrary Storage WriteN/APASS
    Arbitrary JumpN/APASS
    Delegate Call to Untrusted ContractN/APASS
    Dependence on Predictable VariablesN/APASS
    Deprecated OpcodesN/APASS
    Ether 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

    Token/Presale Contract


    Smart Contract Graph

    Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
     
     + [Lib] Math 
        - [Int] max
        - [Int] min
        - [Int] average
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Pub]  #
        - [Int] _msgSender
        - [Int] _msgData
    
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] isOwner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     +  BasicToken (IERC20, Context, Ownable)
        - [Pub] approve #
        - [Pub] allowance
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Int] _approve #
        - [Pub] increaseAllowance #
        - [Pub] decreaseAllowance #
        - [Pub] checkInvestedETH
    
     +  StandardToken (BasicToken)
        - [Pub] setupContract #
           - modifiers: onlyOwner
        - [Pub] transfer #
        - [Pub] transferFrom #
        - [Int] findOnePercent
        - [Int] findThreePercent
        - [Int] findFivePercent
        - [Int] _transferSpecial #
        - [Int] _transfer #
    
     +  Configurable 
    
     +  PreSaleToken (StandardToken, Configurable)
        - [Pub]  #
        - [Ext]  ($)
        - [Pub] startPresale #
           - modifiers: onlyOwner
        - [Int] endPresale #
        - [Pub] finalizePresale #
           - modifiers: onlyOwner
    
     +  Presale (PreSaleToken)
    	
    							

    Click here to download the source code as a .sol file.

    
    /**
     *Submitted for verification at Etherscan.io on 2021-02-23
    */
    
    /*  _______________________________________________________________
                          PRESALE DETAILS |
    
        1. Presale Start    : 1614171600 [FEB 24, 2021] [1 PM UTC]
        2. Presale End      : 1614344400 [FEB 26, 2021] [1 PM UTC]  
        3. Base Price       : 1 ETH = 1000 APEAPE                  
        4. Max Purchase     : 3 ETH                                
        5. HARD CAP         : 60 ETH                               
        _______________________________________________________________
                        | APE APE DETAILS |
    
        1. Total Supply       : 100K                                |
        2. APEAPE Unlock Time : 1614430800 [FEB 27, 2021] [1PM UTC] | *
        3. Burn               : 1%                                  | 
        4. Total Supply       : 100K                                |
        5. Last20 Tx Fee      : 1%                                  |
        6. Reward Collector   : 3% from last 7 days token transfers | 
        _______________________________________________________________
        
        -Codezeros Developers
        -https://www.codezeros.com/
        _______________________________________________________________
    */
    
    
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    
    library Math {
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
    
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
        }
    }
    
    library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
    
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
    
            return c;
        }
    
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
    
        function div(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            uint256 c = a / b;
    
            return c;
        }
    
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
    
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    contract Context {
        constructor() {}
    
        function _msgSender() internal view returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view returns (bytes memory) {
            this;
            return msg.data;
        }
    }
    
    interface IERC20 {
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address account) external view returns (uint256);
    
        function transfer(address recipient, uint256 amount)
            external
            returns (bool);
    
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
    
        function approve(address spender, uint256 amount) external returns (bool);
    
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
    }
    
    contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
    
        constructor() {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        function owner() public view returns (address) {
            return _owner;
        }
    
        modifier onlyOwner() {
            require(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        function _transferOwnership(address newOwner) internal {
            require(
                newOwner != address(0),
                "Ownable: new owner is the zero address"
            );
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    abstract contract BasicToken is IERC20, Context, Ownable {
        using SafeMath for uint256;
        uint256 public _totalSupply;
        mapping(address => uint256) balances_;
        mapping(address => uint256) ethBalances;
        mapping(address => mapping(address => uint256)) internal _allowances;
    
        uint256 public unlockDuration = 72 hours;                               // ----| Lock transfers for non-owner |------
    
        uint256 public startTime = 1614171600;                                  // ------| Deploy Timestamp |--------
        uint256 public rewardDispatchStartTime = startTime.add(unlockDuration); // ------| Start after 72 hours |--------
    
        function approve(address spender, uint256 amount)
            public
            virtual
            override
            returns (bool)
        {
            _approve(msg.sender, spender, amount);
            return true;
        }
    
        function allowance(address owner, address spender)
            public
            view
            virtual
            override
            returns (uint256)
        {
            return _allowances[owner][spender];
        }
    
        function totalSupply() public view override returns (uint256) {
            return _totalSupply;
        }
    
        function balanceOf(address account) public view override returns (uint256) {
            return balances_[account];
        }
    
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
    
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
    
        function increaseAllowance(address spender, uint256 addedValue)
            public
            virtual
            returns (bool)
        {
            _approve(
                msg.sender,
                spender,
                _allowances[msg.sender][spender].add(addedValue)
            );
            return true;
        }
    
        function decreaseAllowance(address spender, uint256 subtractedValue)
            public
            virtual
            returns (bool)
        {
            _approve(
                msg.sender,
                spender,
                _allowances[msg.sender][spender].sub(
                    subtractedValue,
                    "ERC20: decreased allowance below zero"
                )
            );
            return true;
        }
    
        function checkInvestedETH(address who) public view returns (uint256) {
            return ethBalances[who];
        }
    }
    
    abstract contract StandardToken is BasicToken {
        using SafeMath for uint256;
    
        uint256 public lastTwentyTxReward = 0;               //----| Stores 1 % form last 20 transactions|-----
        uint256 public tokensToBurn;                         //------| Burns 1 % token on each transfer |------
        uint256 public StakingContractFee = 0;
        uint256 public transferCounter = 0;
    
        address public stakingContract;
        address public rewardCollector;
    
        uint256 public realStakingContractFee;               
    
        function setupContract(address _stakingContract, address _rewardCollector) public onlyOwner {
           
            require(stakingContract == address(0),"Staking Contract is already set");
            require(rewardCollector == address(0),"Reward Collector Contract is already set");
    
            stakingContract = _stakingContract;
            rewardCollector = _rewardCollector;
        }
    
        function transfer(address recipient, uint256 amount)
            public
            virtual
            override
            returns (bool)
        {
            
            if (msg.sender == stakingContract ||  msg.sender == rewardCollector || msg.sender == owner()) {
                                            
                _transferSpecial(msg.sender, recipient, amount);
    
            } else {
    
                _transfer(msg.sender, recipient, amount);
    
            }
    
            return true;
        }
    
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) public virtual override returns (bool) {
    
            if (msg.sender == stakingContract ||  msg.sender == rewardCollector || msg.sender == owner()) {
    
                _transferSpecial(sender, recipient, amount);
    
            } else {
              
                _transfer(sender, recipient, amount);
            }
    
            _approve(
                sender,
                msg.sender,
                _allowances[sender][msg.sender].sub(
                    amount,
                    "ERC20: transfer amount exceeds allowance"
                )
            );
            return true;
        }
    
        function findOnePercent(uint256 amount) internal pure returns (uint256) {
            return amount.mul(10).div(1000);
        }
    
        function findThreePercent(uint256 amount) internal pure returns (uint256) {
            return amount.mul(30).div(1000);
        }
    
        function findFivePercent(uint256 amount) internal pure returns (uint256) {
            return amount.mul(50).div(1000);
        }
    
        function _transferSpecial(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(
                sender != address(0),
                "ERC20 Special: transfer from the zero address"
            );
            require(
                recipient != address(0),
                "ERC20 Special: transfer to the zero address"
            );
    
            balances_[sender] = balances_[sender].sub(
                amount,
                "ERC20 Special: transfer amount exceeds balance"
            );
            balances_[recipient] = balances_[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
    
        function _transfer(
            address sender,
            address recipient,
            uint256 amount
        ) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            require(stakingContract != address(0), "Staking contract is not set");
            require(rewardCollector != address(0), "RewardCollector contract is not set");
    
            require(
                block.timestamp >= startTime.add(unlockDuration), "Tokens not unlocked yet");
    
    
            tokensToBurn = findOnePercent(amount);                               //---| 1% ===> Burn |-----------------
            lastTwentyTxReward = lastTwentyTxReward + findOnePercent(amount);    //---| 1% ===> Last20Tx Collection|---
            StakingContractFee = StakingContractFee + findThreePercent(amount);  //---| 3% ===> Reward Collector |-----
    
            uint256 tokensToTransfer = amount.sub(findFivePercent(amount), "overflow");        //---| Net Amount Received |----------
    
            _totalSupply = _totalSupply.sub(tokensToBurn);                       //---| Remove from Total Supply |-----
    
            balances_[sender] = balances_[sender].sub(
                amount,
                "ERC20: transfer amount exceeds balance"
            );
    
            balances_[recipient] = balances_[recipient].add(tokensToTransfer);
    
            transferCounter = transferCounter + 1;
    
            if (transferCounter == 20) {
                
                balances_[sender] = balances_[sender].add(lastTwentyTxReward); //---| Rewards last 20 Transactions |-----
                transferCounter = 0;
    
                emit Transfer(address(0), sender, lastTwentyTxReward);
                lastTwentyTxReward = 0;
            }
    
            if (block.timestamp > rewardDispatchStartTime.add(7 days)) {   //-|Transfer rewards to RewardCollector every 7 days |--
                balances_[rewardCollector] = balances_[rewardCollector].add(
                    StakingContractFee
                );
    
                realStakingContractFee = StakingContractFee;
                StakingContractFee = 0;
                rewardDispatchStartTime = block.timestamp;
    
                emit Transfer(
                    address(this),
                    rewardCollector,
                    realStakingContractFee
                );
            }
    
            emit Transfer(sender, recipient, tokensToTransfer);
            emit Transfer(sender, address(0), tokensToBurn);
        }
        
    }
    
    contract Configurable {
        uint256 public cap = 60000 * 10**18;                 //---------| Tokens for Presale |---------
        uint256 public basePrice = 1000 * 10**18;            //-----| 1 ETH = 1000 Tokens |---------
        uint256 public tokensSold;
        uint256 public tokenReserve = 100000 * 10**18;       //-----------| Total Supply = 100 K |------
        uint256 public remainingTokens;
    }
    
    contract PreSaleToken is StandardToken, Configurable { 
        using SafeMath for uint256;
        enum Phases {none, start, end}
        Phases public currentPhase;
    
        constructor() {
           
            currentPhase = Phases.none;
            balances_[owner()] = balances_[owner()].add(tokenReserve);
            _totalSupply = _totalSupply.add(tokenReserve);
            remainingTokens = cap;
            emit Transfer(address(this), owner(), tokenReserve);
        }
    
        receive() external payable {
            require(
                currentPhase == Phases.start,
                "The presale has not started yet"
            );
            require(remainingTokens > 0, "Presale token limit reached");
    
            uint256 weiAmount = msg.value;
            uint256 tokens = weiAmount.mul(basePrice).div(1 ether);
        
            ethBalances[msg.sender] = ethBalances[msg.sender].add(weiAmount); // Track each user investments
            ethBalances[address(this)] = ethBalances[address(this)].add(weiAmount); // Track this contract's funds
    
            require(
                ethBalances[msg.sender] <= 3e18,
                "You are exceeding max 3 ETH of purchase"
            );
            require(
                ethBalances[address(this)] <= 60e18,
                "Sorry! target amount of 60 ETH has been achieved"
            );
    
            if (tokensSold.add(tokens) > cap) {
                revert("Exceeding limit of presale tokens");
            }
    
            tokensSold = tokensSold.add(tokens); // counting tokens sold
            remainingTokens = cap.sub(tokensSold);
    
            balances_[owner()] = balances_[owner()].sub(
                tokens,
                "ERC20: transfer amount exceeds balance"
            );
    
            balances_[msg.sender] = balances_[msg.sender].add(tokens);
    
            emit Transfer(address(this), msg.sender, tokens);
    
            payable(owner()).transfer(weiAmount);
        }
    
        function startPresale() public onlyOwner {
            require(currentPhase != Phases.end, "The coin offering has ended");
            currentPhase = Phases.start;
        }
    
        function endPresale() public onlyOwner {
            require(currentPhase != Phases.end, "The presale has ended");
            currentPhase = Phases.end;
        }
    
    }
    
    contract Presale is PreSaleToken {
        string public name = "ApeApe Finance";
        string public symbol = "APEAPE";
        uint32 public decimals = 18;
    }
    

    Staking Contract


    Smart Contract Graph

    Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
     
     + [Lib] Math 
        - [Int] max
        - [Int] min
        - [Int] average
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Int]  #
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Int]  #
        - [Pub] owner
        - [Pub] isOwner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     + [Lib] Address 
        - [Int] isContract
        - [Int] toPayable
        - [Int] sendValue #
    
     + [Lib] SafeERC20 
        - [Int] safeTransfer #
        - [Int] safeTransferFrom #
        - [Int] safeApprove #
        - [Int] safeIncreaseAllowance #
        - [Int] safeDecreaseAllowance #
        - [Prv] callOptionalReturn #
    
     +  LPTokenWrapper (Ownable)
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Pub] stake #
        - [Pub] withdraw #
    
     +  Staking (LPTokenWrapper)
        - [Pub] setupRewardCollector #
        - [Pub] lastTimeRewardApplicable
        - [Pub] rewardPerToken
        - [Pub] earned
        - [Pub] stake #
           - modifiers: updateReward,checkStart
        - [Pub] withdraw #
           - modifiers: updateReward,checkStart
        - [Ext] exit #
        - [Pub] getReward #
           - modifiers: updateReward,checkStart
        - [Pub] permitNotifyReward
        - [Int] calculateTenPercent
        - [Int] calculateNinetyPercent
        - [Ext] notifyRewardAmount #
           - modifiers: updateReward
    
    							

    Click here to download the source code as a .sol file.

    
    
    /**
     *Submitted for verification at Etherscan.io on 2021-02-23
    */
    
    /*  __________________________________________________________________________
                                  STAKING POOL DETAILS |
    
        1.StartTime                   : Starts immediately when deployed.        
        2.Reward Cycle                : 7 days                                   
        3.StartTime and Reward Reset  : Once someone notify reward after 7 days.
        __________________________________________________________________________
        
        -Codezeros Developers
        -https://www.codezeros.com/
        __________________________________________________________________________
    */
    
    
    pragma solidity ^0.5.0;
    
    
    library Math {
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
    
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
        }
    }
    
    library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
    
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
    
            return c;
        }
    
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
    
        function div(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            uint256 c = a / b;
    
            return c;
        }
    
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
    
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    contract Context {
        constructor() internal {}
    
        function _msgSender() internal view returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view returns (bytes memory) {
            this;
            return msg.data;
        }
    }
    
    contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
    
        constructor() internal {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        function owner() public view returns (address) {
            return _owner;
        }
    
        modifier onlyOwner() {
            require(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        function _transferOwnership(address newOwner) internal {
            require(
                newOwner != address(0),
                "Ownable: new owner is the zero address"
            );
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    interface IERC20 {
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address account) external view returns (uint256);
    
        function transfer(address recipient, uint256 amount)
            external
            returns (bool);
    
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
    
        function approve(address spender, uint256 amount) external returns (bool);
    
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
    }
    
    library Address {
        function isContract(address account) internal view returns (bool) {
            bytes32 codehash;
            bytes32 accountHash =
                0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    
            assembly {
                codehash := extcodehash(account)
            }
            return (codehash != 0x0 && codehash != accountHash);
        }
    
        function toPayable(address account)
            internal
            pure
            returns (address payable)
        {
            return address(uint160(account));
        }
    
        function sendValue(address payable recipient, uint256 amount) internal {
            require(
                address(this).balance >= amount,
                "Address: insufficient balance"
            );
    
            (bool success, ) = recipient.call.value(amount)("");
            require(
                success,
                "Address: unable to send value, recipient may have reverted"
            );
        }
    }
    
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
    
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 value
        ) internal {
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.transfer.selector, to, value)
            );
        }
    
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 value
        ) internal {
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
            );
        }
    
        function safeApprove(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.approve.selector, spender, value)
            );
        }
    
        function safeIncreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance =
                token.allowance(address(this), spender).add(value);
            callOptionalReturn(
                token,
                abi.encodeWithSelector(
                    token.approve.selector,
                    spender,
                    newAllowance
                )
            );
        }
    
        function safeDecreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance =
                token.allowance(address(this), spender).sub(
                    value,
                    "SafeERC20: decreased allowance below zero"
                );
            callOptionalReturn(
                token,
                abi.encodeWithSelector(
                    token.approve.selector,
                    spender,
                    newAllowance
                )
            );
        }
    
        function callOptionalReturn(IERC20 token, bytes memory data) private {
            require(address(token).isContract(), "SafeERC20: call to non-contract");
    
            (bool success, bytes memory returndata) = address(token).call(data);
            require(success, "SafeERC20: low-level call failed");
    
            if (returndata.length > 0) {
                // Return data is optional
    
                require(
                    abi.decode(returndata, (bool)),
                    "SafeERC20: ERC20 operation did not succeed"
                );
            }
        }
    }
    
    contract LPTokenWrapper is Ownable {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
    
        IERC20 public APE_APE = IERC20(0xCBb29e3e89998E49abD82B9623a088431F6a5B3b); // Must be changed with Presale
    
        uint256 private _totalSupply;
        mapping(address => uint256) private _balances;
    
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
    
        function balanceOf(address account) public view returns (uint256) {
            return _balances[account];
        }
    
        function stake(uint256 amount) public {
            _totalSupply = _totalSupply.add(amount);
            _balances[msg.sender] = _balances[msg.sender].add(amount);
            APE_APE.safeTransferFrom(msg.sender, address(this), amount);
        }
    
        function withdraw(uint256 amount) public {
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            APE_APE.safeTransfer(msg.sender, amount);
        }
    }
    
    contract Staking is LPTokenWrapper {
        IERC20 public ape = IERC20(0xCBb29e3e89998E49abD82B9623a088431F6a5B3b); // Must be changed with Presale
        uint256 public constant duration = 7 days;                          
    
        uint256 public starttime = block.timestamp;              //-----| Starts immediately after deploy |-----                           
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        address public rewardCollector;
        
        uint256 rewardAmount = 0;
    
     
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
    
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event Rewarded(address indexed from, address indexed to, uint256 value);
    
        modifier checkStart() {
            require(
                block.timestamp >= starttime,
                "ApeApe staking pool not started yet."
            );
            _;
        }
    
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
        }
        
        function setupRewardCollector(address _rewardCollector) public returns (bool) {
            require(rewardCollector == address(0), "Reward Collector is already set");
            rewardCollector = _rewardCollector;
            
            return true;
        }
    
    
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
    
        function rewardPerToken() public view returns (uint256) {
            if (totalSupply() == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable()
                        .sub(lastUpdateTime)
                        .mul(rewardRate)
                        .mul(1e18)
                        .div(totalSupply())
                );
        }
    
        function earned(address account) public view returns (uint256) {
            return
                balanceOf(account)
                    .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                    .div(1e18)
                    .add(rewards[account]);
        }
    
        function stake(uint256 amount) public updateReward(msg.sender) checkStart {
            require(amount > 0, "Cannot stake 0");
            super.stake(amount);
        }
    
        function withdraw(uint256 amount)
            public
            updateReward(msg.sender)
        {
            require(amount > 0, "Cannot withdraw 0");
            super.withdraw(amount);
        }
    
        // withdraw stake and get rewards at once
        function exit() external {
            withdraw(balanceOf(msg.sender));
            getReward();
        }
    
        function getReward() public updateReward(msg.sender){
            uint256 reward = earned(msg.sender);
    
            if (reward > 0) {
                rewards[msg.sender] = 0;
            
                ape.safeTransfer(msg.sender, reward);
            }
    
            
        }
    
        function permitNotifyReward() public view returns (bool) {    //-----| If current reward session has completed |---------
            
            if(block.timestamp > starttime.add(duration)){
               return true;
            }
             
        }
    
        function calculateTenPercent(uint256 amount)
            internal
            pure
            returns (uint256)
        {
            return amount.mul(100).div(1000);
        }
    
        function calculateNinetyPercent(uint256 amount)
            internal
            pure
            returns (uint256)
        {
            return amount.mul(900).div(1000);
        }
    
        function notifyRewardAmount() external updateReward(address(0)) returns (bool){
            require(rewardCollector != address(0), "RewardCollector contract is not set");
            require(msg.sender == rewardCollector, "Only rewardCollector contract can call this method");
    
            require(permitNotifyReward() == true, "Cannot notify until 7 days are completed");
            
            rewardAmount = calculateNinetyPercent(ape.balanceOf(rewardCollector));
            rewardRate = rewardAmount.div(duration);
           
            lastUpdateTime = block.timestamp;
            starttime = block.timestamp; 
            periodFinish = block.timestamp.add(duration);
    
            return true;
    
        }
    }
    

    RewardCollector Contract


    Smart Contract Graph

    Contract Inheritance

    
     ($) = payable function
     # = non-constant function
     
     Int = Internal
     Ext = External
     Pub = Public
     
     + [Lib] Math 
        - [Int] max
        - [Int] min
        - [Int] average
    
     + [Lib] SafeMath 
        - [Int] add
        - [Int] sub
        - [Int] sub
        - [Int] mul
        - [Int] div
        - [Int] div
        - [Int] mod
        - [Int] mod
    
     +  Context 
        - [Int]  #
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Int]  #
        - [Pub] owner
        - [Pub] isOwner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     + [Lib] Address 
        - [Int] isContract
        - [Int] toPayable
        - [Int] sendValue #
    
     + [Lib] SafeERC20 
        - [Int] safeTransfer #
        - [Int] safeTransferFrom #
        - [Int] safeApprove #
        - [Int] safeIncreaseAllowance #
        - [Int] safeDecreaseAllowance #
        - [Prv] callOptionalReturn #
    
     + [Int] Staking 
        - [Ext] notifyRewardAmount #
    
     +  RewardCollector (Ownable, Staking)
        - [Pub]  #
        - [Pub] rewardDeposits
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Pub] calculateTenPercent
        - [Pub] calculateNinetyPercent
        - [Pub] notifyRewardAmount #
        - [Pub] notifyExternal #
    
    							

    Click here to download the source code as a .sol file.

    
    /**
     *Submitted for verification at Etherscan.io on 2021-02-23
    */
    
    pragma solidity ^0.5.0;
    
    /*  ____________________________________________________________________
    
        1. Notify Reward and send to staking : 90% of this reward deposits  
        2. Total Rewards    : 90% of this contract balance                  
        3. Notifier Rewards : 10 % of this contract balance                 
        ____________________________________________________________________
        
        -Codezeros Developers
        -https://www.codezeros.com/
        ____________________________________________________________________
    
    */
    
    library Math {
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
    
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
        }
    }
    
    library SafeMath {
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
    
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
    
            return c;
        }
    
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
    
        function div(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            uint256 c = a / b;
    
            return c;
        }
    
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
    
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    contract Context {
        constructor() internal {}
    
        function _msgSender() internal view returns (address payable) {
            return msg.sender;
        }
    
        function _msgData() internal view returns (bytes memory) {
            this;
            return msg.data;
        }
    }
    
    contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
    
        constructor() internal {
            _owner = msg.sender;
            emit OwnershipTransferred(address(0), _owner);
        }
    
        function owner() public view returns (address) {
            return _owner;
        }
    
        modifier onlyOwner() {
            require(isOwner(), "Ownable: caller is not the owner");
            _;
        }
    
        function isOwner() public view returns (bool) {
            return msg.sender == _owner;
        }
    
        function renounceOwnership() public onlyOwner {
            emit OwnershipTransferred(_owner, address(0));
            _owner = address(0);
        }
    
        function transferOwnership(address newOwner) public onlyOwner {
            _transferOwnership(newOwner);
        }
    
        function _transferOwnership(address newOwner) internal {
            require(
                newOwner != address(0),
                "Ownable: new owner is the zero address"
            );
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
        }
    }
    
    interface IERC20 {
        // function totalSupply() external view returns (uint256);
    
        function balanceOf(address account) external view returns (uint256);
    
        function transfer(address recipient, uint256 amount)
            external
            returns (bool);
    
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
    
        function approve(address spender, uint256 amount) external returns (bool);
    
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
    }
    
    library Address {
        function isContract(address account) internal view returns (bool) {
            bytes32 codehash;
            bytes32 accountHash =
                0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    
            assembly {
                codehash := extcodehash(account)
            }
            return (codehash != 0x0 && codehash != accountHash);
        }
    
        function toPayable(address account)
            internal
            pure
            returns (address payable)
        {
            return address(uint160(account));
        }
    
        function sendValue(address payable recipient, uint256 amount) internal {
            require(
                address(this).balance >= amount,
                "Address: insufficient balance"
            );
    
            (bool success, ) = recipient.call.value(amount)("");
            require(
                success,
                "Address: unable to send value, recipient may have reverted"
            );
        }
    }
    
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
    
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 value
        ) internal {
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.transfer.selector, to, value)
            );
        }
    
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 value
        ) internal {
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
            );
        }
    
        function safeApprove(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            callOptionalReturn(
                token,
                abi.encodeWithSelector(token.approve.selector, spender, value)
            );
        }
    
        function safeIncreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance =
                token.allowance(address(this), spender).add(value);
            callOptionalReturn(
                token,
                abi.encodeWithSelector(
                    token.approve.selector,
                    spender,
                    newAllowance
                )
            );
        }
    
        function safeDecreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance =
                token.allowance(address(this), spender).sub(
                    value,
                    "SafeERC20: decreased allowance below zero"
                );
            callOptionalReturn(
                token,
                abi.encodeWithSelector(
                    token.approve.selector,
                    spender,
                    newAllowance
                )
            );
        }
    
        function callOptionalReturn(IERC20 token, bytes memory data) private {
            require(address(token).isContract(), "SafeERC20: call to non-contract");
    
            (bool success, bytes memory returndata) = address(token).call(data);
            require(success, "SafeERC20: low-level call failed");
    
            if (returndata.length > 0) {
               
    
                require(
                    abi.decode(returndata, (bool)),
                    "SafeERC20: ERC20 operation did not succeed"
                );
            }
        }
    }
    
    interface Staking {
        function notifyRewardAmount() external returns (bool);
    
    }
    
    contract RewardCollector is Ownable, Staking {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
    
        Staking stakingInstance;
    
        uint256 notifierFee;
        uint256 notifyAmount;
    
    
        IERC20 public APE ;
    
        constructor(address _presale, address _staking) public {
            
            APE = IERC20(_presale);
            stakingInstance = Staking(_staking);
    
    
        }
    
        function rewardDeposits() public view returns (uint256) {
            return APE.balanceOf(address(this));
        }
    
        function balanceOf(address account) public view returns (uint256) {
            return APE.balanceOf(account);
        }
    
        function calculateTenPercent(uint256 amount)
            public
            pure
            returns (uint256)
        {
            return amount.mul(100).div(1000);
        }
    
        function calculateNinetyPercent(uint256 amount)
            public
            pure
            returns (uint256)
        {
            return amount.mul(900).div(1000);
        }
    
        function notifyRewardAmount() public returns (bool) {
            return stakingInstance.notifyRewardAmount();
        }
    
        function notifyExternal() public returns (bool) {
    
            notifyRewardAmount();
            notifierFee = calculateTenPercent(rewardDeposits());       //-----| Notifier Reward |------------
            notifyAmount = calculateNinetyPercent(rewardDeposits());   //-----| For Notify Purpose |---------
    
            APE.safeTransfer(msg.sender, notifierFee);                 //-----| Send notifier reward |-------
            APE.safeTransfer(address(stakingInstance), notifyAmount);           //-----| Send to staking contract|-----
    
    
            return true;
    
        }
    }