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 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 |
ApeApe.finance Token & Staking - Smart Contract Audit Report
Summary
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.
Notes on the Contracts:Audit Findings Summary:
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.
- 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.
Token/Presale Contract
($) = 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
($) = 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
($) = 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;
}
}