diff --git a/contracts/MasterChefV2.sol b/contracts/MasterChefV2.sol new file mode 100644 index 0000000..99fdec3 --- /dev/null +++ b/contracts/MasterChefV2.sol @@ -0,0 +1,348 @@ +pragma solidity ^0.6.12; + +import "./lib/ReentrancyGuard.sol"; +import "./lib/SafeBEP20.sol"; +import "./MochaToken.sol"; + + +// MasterChef is the master of Brew. He can make Brew and he is a fair guy. +// +// Note that it's ownable and the owner wields tremendous power. The ownership +// will be transferred to a governance smart contract once BREW is sufficiently +// distributed and the community can show to govern itself. +// +// Have fun reading it. Hopefully it's bug-free. God bless. +contract MasterChefV2 is Ownable, ReentrancyGuard { + using SafeMath for uint256; + using SafeBEP20 for IBEP20; + + // Info of each user. + struct UserInfo { + uint256 amount; // How many LP tokens the user has provided. + uint256 rewardDebt; // Reward debt. See explanation below. + // + // We do some fancy math here. Basically, any point in time, the amount of BREWs + // entitled to a user but is pending to be distributed is: + // + // pending reward = (user.amount * pool.accBrewPerShare) - user.rewardDebt + // + // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: + // 1. The pool's `accBrewPerShare` (and `lastRewardBlock`) gets updated. + // 2. User receives the pending reward sent to his/her address. + // 3. User's `amount` gets updated. + // 4. User's `rewardDebt` gets updated. + } + + // Info of each pool. + struct PoolInfo { + IBEP20 lpToken; // Address of LP token contract. + uint256 allocPoint; // How many allocation points assigned to this pool. BREWs to distribute per block. + uint256 lastRewardBlock; // Last block number that BREWs distribution occurs. + uint256 accBrewPerShare; // Accumulated BREWs per share, times 1e12. See below. + uint16 depositFeeBP; // Deposit fee in basis points + } + + // The BREW TOKEN! + MochaToken public brew; + // Dev address. + address public devaddr; + // BREW tokens created per block. + uint256 public brewPerBlock; + // Bonus muliplier for early brew makers. + // Deposit Fee address + address public feeAddress; + + // Info of each pool. + PoolInfo[] public poolInfo; + // Info of each user that stakes LP tokens. + mapping(uint256 => mapping(address => UserInfo)) public userInfo; + mapping(IBEP20 => bool) public poolExistence; + // Total allocation points. Must be the sum of all allocation points in all pools. + uint256 public totalAllocPoint = 0; + // The block number when BREW mining starts. + uint256 public startBlock; + // The totalSupply + uint256 public totalSupply; + uint256 public constant HARD_CAP = 450000e18; + uint256 public constant MAX_FEE = 10000; + uint256 public burnFee = 500; + + event Deposit(address indexed user, uint256 indexed pid, uint256 amount); + event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); + event EmergencyWithdraw( + address indexed user, + uint256 indexed pid, + uint256 amount + ); + event SetFeeAddress(address indexed user, address indexed newAddress); + event SetDevAddress(address indexed user, address indexed newAddress); + event UpdateEmissionRate(address indexed user, uint256 brewPerBlock); + event SetBurnFee(uint256 newFee); + + constructor( + MochaToken _brew, + address _devaddr, + address _feeAddress, + uint256 _brewPerBlock, + uint256 _startBlock + ) public { + brew = _brew; + devaddr = _devaddr; + feeAddress = _feeAddress; + brewPerBlock = _brewPerBlock; + startBlock = _startBlock; + } + + modifier nonDuplicated(IBEP20 _lpToken) { + require(poolExistence[_lpToken] == false, "nonDuplicated: duplicated"); + _; + } + + modifier isPoolExist(uint256 _pid) { + require(_pid < poolLength(), "isPoolExist: pool not exist"); + _; + } + + function poolLength() public view returns (uint256) { + return poolInfo.length; + } + + // Add a new lp to the pool. Can only be called by the owner. + function add( + uint256 _allocPoint, + IBEP20 _lpToken, + uint16 _depositFeeBP, + bool _withUpdate + ) external onlyOwner nonDuplicated(_lpToken) { + require( + _depositFeeBP <= 500, + "add: invalid deposit fee basis points" //max 5% + ); + if (_withUpdate) { + massUpdatePools(); + } + uint256 lastRewardBlock = + block.number > startBlock ? block.number : startBlock; + totalAllocPoint = totalAllocPoint.add(_allocPoint); + poolExistence[_lpToken] = true; + poolInfo.push( + PoolInfo({ + lpToken: _lpToken, + allocPoint: _allocPoint, + lastRewardBlock: lastRewardBlock, + accBrewPerShare: 0, + depositFeeBP: _depositFeeBP + }) + ); + } + + // Update the given pool's BREW allocation point and deposit fee. Can only be called by the owner. + function set( + uint256 _pid, + uint256 _allocPoint, + uint16 _depositFeeBP, + bool _withUpdate + ) external onlyOwner isPoolExist(_pid) { + require( + _depositFeeBP <= 500, + "set: invalid deposit fee basis points" // max 5% + ); + if (_withUpdate) { + massUpdatePools(); + } + totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add( + _allocPoint + ); + poolInfo[_pid].allocPoint = _allocPoint; + poolInfo[_pid].depositFeeBP = _depositFeeBP; + } + + // Return reward multiplier over the given _from to _to block. + function getMultiplier(uint256 _from, uint256 _to) public pure returns (uint256) { + return _to.sub(_from); + } + + // View function to see pending BREWs on frontend. + function pendingBrew(uint256 _pid, address _user) + external + isPoolExist(_pid) + view + returns (uint256) + { + PoolInfo storage pool = poolInfo[_pid]; + UserInfo storage user = userInfo[_pid][_user]; + uint256 accBrewPerShare = pool.accBrewPerShare; + uint256 lpSupply = pool.lpToken.balanceOf(address(this)); + if (block.number > pool.lastRewardBlock && lpSupply != 0) { + uint256 multiplier = + getMultiplier(pool.lastRewardBlock, block.number); + uint256 brewReward = + multiplier.mul(brewPerBlock).mul(pool.allocPoint).div( + totalAllocPoint + ); + if(totalSupply.add(brewReward) >= HARD_CAP){ + brewReward = HARD_CAP.sub(totalSupply); + } + brewReward = brewReward.sub(brewReward.div(10)); + accBrewPerShare = accBrewPerShare.add( + brewReward.mul(1e12).div(lpSupply) + ); + } + return user.amount.mul(accBrewPerShare).div(1e12).sub(user.rewardDebt); + } + + // Update reward variables for all pools. Be careful of gas spending! + function massUpdatePools() public { + uint256 length = poolInfo.length; + for (uint256 pid = 0; pid < length; ++pid) { + updatePool(pid); + } + } + + // Update reward variables of the given pool to be up-to-date. + function updatePool(uint256 _pid) public isPoolExist(_pid) { + PoolInfo storage pool = poolInfo[_pid]; + if (block.number <= pool.lastRewardBlock) { + return; + } + uint256 lpSupply = pool.lpToken.balanceOf(address(this)); + if (lpSupply == 0 || pool.allocPoint == 0 || brewPerBlock == 0|| totalSupply == HARD_CAP ) { + pool.lastRewardBlock = block.number; + return; + } + uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); + uint256 brewReward = + multiplier.mul(brewPerBlock).mul(pool.allocPoint).div(totalAllocPoint); + if(totalSupply.add(brewReward) >= HARD_CAP){ + brewReward = HARD_CAP.sub(totalSupply); + brewPerBlock = 0; + } + totalSupply = totalSupply.add(brewReward); + brew.mint(devaddr, brewReward.div(10)); + brewReward = brewReward.sub(brewReward.div(10)); + brew.mint(address(this), brewReward); + pool.accBrewPerShare = pool.accBrewPerShare.add( + brewReward.mul(1e12).div(lpSupply) + ); + pool.lastRewardBlock = block.number; + } + + // Deposit LP tokens to MasterChef for BREW allocation. + function deposit(uint256 _pid, uint256 _amount) external nonReentrant isPoolExist(_pid) { + PoolInfo storage pool = poolInfo[_pid]; + UserInfo storage user = userInfo[_pid][msg.sender]; + updatePool(_pid); + if (user.amount > 0) { + uint256 pending = + user.amount.mul(pool.accBrewPerShare).div(1e12).sub( + user.rewardDebt + ); + if (pending > 0) { + user.rewardDebt = user.amount.mul(pool.accBrewPerShare).div(1e12); + uint256 fee = (pending.mul(burnFee)).div(MAX_FEE); + if(safeBrewBurn(fee)){ + safeBrewTransfer(msg.sender, (pending.sub(fee))); + } + } + } + if (_amount > 0) { + pool.lpToken.safeTransferFrom( + address(msg.sender), + address(this), + _amount + ); + if (pool.depositFeeBP > 0) { + uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000); + pool.lpToken.safeTransfer(feeAddress, depositFee); + user.amount = user.amount.add(_amount).sub(depositFee); + } else { + user.amount = user.amount.add(_amount); + } + } + user.rewardDebt = user.amount.mul(pool.accBrewPerShare).div(1e12); + emit Deposit(msg.sender, _pid, _amount); + } + + // Withdraw LP tokens from MasterChef. + function withdraw(uint256 _pid, uint256 _amount) external nonReentrant isPoolExist(_pid) { + PoolInfo storage pool = poolInfo[_pid]; + UserInfo storage user = userInfo[_pid][msg.sender]; + require(user.amount >= _amount, "withdraw: not good"); + updatePool(_pid); + uint256 pending = + user.amount.mul(pool.accBrewPerShare).div(1e12).sub( + user.rewardDebt + ); + if (pending > 0) { + user.rewardDebt = user.amount.mul(pool.accBrewPerShare).div(1e12); + uint256 fee = (pending.mul(burnFee)).div(MAX_FEE); + if(safeBrewBurn(fee)){ + safeBrewTransfer(msg.sender, (pending.sub(fee))); + } + } + if (_amount > 0) { + user.amount = user.amount.sub(_amount); + pool.lpToken.safeTransfer(address(msg.sender), _amount); + } + user.rewardDebt = user.amount.mul(pool.accBrewPerShare).div(1e12); + emit Withdraw(msg.sender, _pid, _amount); + } + + // Withdraw without caring about rewards. EMERGENCY ONLY. + function emergencyWithdraw(uint256 _pid) external nonReentrant isPoolExist(_pid) { + PoolInfo storage pool = poolInfo[_pid]; + UserInfo storage user = userInfo[_pid][msg.sender]; + uint256 amount = user.amount; + user.amount = 0; + user.rewardDebt = 0; + pool.lpToken.safeTransfer(address(msg.sender), amount); + emit EmergencyWithdraw(msg.sender, _pid, amount); + } + + // Safe brew transfer function, just in case if rounding error causes pool to not have enough BREWs. + function safeBrewTransfer(address _to, uint256 _amount) internal { + uint256 brewBal = brew.balanceOf(address(this)); + bool transferSuccess = false; + if (_amount > brewBal) { + transferSuccess = brew.transfer(_to, brewBal); + } else { + transferSuccess = brew.transfer(_to, _amount); + } + require(transferSuccess, "safeBrewTransfer: transfer failed"); + } + + function safeBrewBurn(uint256 _amount) internal returns(bool burnSuccess) { + uint256 brewBal = brew.balanceOf(address(this)); + burnSuccess = false; + if (_amount < brewBal) { + burnSuccess = brew.burn(address(this), _amount); + } + } + + // Update dev address by the previous dev. + function dev(address _devaddr) external { + require(msg.sender == devaddr && _devaddr != address(0x0), "dev: wut?"); + devaddr = _devaddr; + emit SetDevAddress(msg.sender, _devaddr); + } + + function setFeeAddress(address _feeAddress) external { + require(msg.sender == feeAddress && _feeAddress != address(0x0), "setFeeAddress: FORBIDDEN"); + feeAddress = _feeAddress; + emit SetFeeAddress(msg.sender, _feeAddress); + } + + //Pancake has to add hidden dummy pools inorder to alter the emission, here we make it simple and transparent to all. + function updateEmissionRate(uint256 _brewPerBlock) external onlyOwner { + massUpdatePools(); + require(_brewPerBlock <= 1e18 ,"invalid brewPerBlock"); + brewPerBlock = _brewPerBlock; + emit UpdateEmissionRate(msg.sender, _brewPerBlock); + } + + function setBurnFee(uint256 _burnFee) external onlyOwner { + require( _burnFee <= 500, "setBurnFee: invalid burnFee fee basis points"); // max 5% + burnFee = _burnFee; + emit SetBurnFee(_burnFee); + } +} \ No newline at end of file diff --git a/contracts/MochaToken.sol b/contracts/MochaToken.sol index a25adee..00ecc17 100644 --- a/contracts/MochaToken.sol +++ b/contracts/MochaToken.sol @@ -13,35 +13,75 @@ contract MochaToken is ERC20("MochaToken", "MOCHA"), Ownable { */ /// @dev This address is will be used to send 0.1% of the MOCHA tokens to. address public rewardAddress; - + /** + * @dev admin address is used to take care of the whitelisting process once + * ownership is transfered to MasterChef + */ + address public admin; + + /** + * @dev burn address is where the tokens are going to be sent to instead + * of being burned. This makes it easier for users to keep track of. + * This isn't changeable for trust and security reasons. + */ + address public burnAddress = 0x000000000000000000000000000000000000dEaD; + /// @dev These are used to calcualte how much of the token is to be burned. uint constant public BURN_FEE = 900; uint constant public REWARD_FEE = 100; uint constant public MAX_FEE = 100000; - uint256 constant public HARD_CAP = 10 ** 7 * 1e18; + uint256 constant public HARD_CAP = 5 * 10 ** 5 * 1e18; + /// @dev List of whitelisted addresses + mapping (address => bool) public whiteList; + + event WhiteList(address account, bool status); + event SetRewardAddress(address caller, address newRewardAddress); + + // Check which makes sure that the HARD_CAP wasn't reached modifier checkHardCap(uint256 amount) { - require(totalSupply().add(amount) <= HARD_CAP, "Supply is greater than 10 mil"); + require(totalSupply().add(amount) <= HARD_CAP, "Supply is greater than 500k!"); _; } + // Check to make sure only the admin can access the WhiteList function + modifier onlyAdmin(){ + require(admin == msg.sender, "Caller not Admin!"); + _; + } - /// @notice This is where the address of the reward pool is set. - function setRewardAddress(address _rewardAddress) public onlyOwner { + /** + * @dev Sets the values for {rewardAddress} which is the address of + * of the reward pool. It's best to set the {admin} address manually + * just incase a deployer contract is used. + */ + constructor (address _rewardAddress, address _admin) public { rewardAddress = _rewardAddress; + admin = _admin; } - - function _transfer(address sender, address recipient, uint256 amount) internal virtual override { - super._transfer(sender, recipient, amount); - (uint256 toBurn, uint256 toReward) = calculateFees(amount); - _burn(recipient, toBurn.add(toReward)); - _mint(rewardAddress, toReward); + /// @notice This is where the address of the reward pool is set. + function setRewardAddress(address _rewardAddress) public onlyAdmin() { + rewardAddress = _rewardAddress; + emit SetRewardAddress(msg.sender,_rewardAddress); + } - emit Transfer(sender, recipient, amount); + /** + * @dev Custom transfer function to burn tokens from users who are not + * white listed. If a user is white listed a normal transfer occurs. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual override { + // We want to burn tokens if both the sender/recipient aren't whitelisted + if(whiteList[sender] == false && whiteList[recipient] == false) { + (uint256 toBurn, uint256 toReward) = calculateFees(amount); + super._transfer(sender, recipient, amount.sub(toBurn.add(toReward))); + super._transfer(sender, burnAddress, toBurn); + super._transfer(sender, rewardAddress, toReward); + } else { + super._transfer(sender, recipient, amount); + } } - - + /// @notice Helper function to calcualte the fees which will be deducted from /// the transaction function calculateFees(uint256 amount) public pure returns (uint256, uint256) { @@ -50,239 +90,25 @@ contract MochaToken is ERC20("MochaToken", "MOCHA"), Ownable { return (toBurn, toReward); } - /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterGardener). + /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterChef). function mint(address _to, uint256 _amount) public onlyOwner checkHardCap(_amount) { _mint(_to, _amount); - _moveDelegates(address(0), _delegates[_to], _amount); - } - - // Copied and modified from YAM code: - // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernanceStorage.sol - // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernance.sol - // Which is copied and modified from COMPOUND: - // https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol - - /// @notice A record of each accounts delegate - mapping (address => address) internal _delegates; - - /// @notice A checkpoint for marking number of votes from a given block - struct Checkpoint { - uint32 fromBlock; - uint256 votes; - } - - /// @notice A record of votes checkpoints for each account, by index - mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; - - /// @notice The number of checkpoints for each account - mapping (address => uint32) public numCheckpoints; - - /// @notice The EIP-712 typehash for the contract's domain - bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); - - /// @notice The EIP-712 typehash for the delegation struct used by the contract - bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - - /// @notice A record of states for signing / validating signatures - mapping (address => uint) public nonces; - - /// @notice An event thats emitted when an account changes its delegate - event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); - - /// @notice An event thats emitted when a delegate account's vote balance changes - event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); - - /** - * @notice Delegate votes from `msg.sender` to `delegatee` - * @param delegator The address to get delegatee for - */ - function delegates(address delegator) - external - view - returns (address) - { - return _delegates[delegator]; } - /** - * @notice Delegate votes from `msg.sender` to `delegatee` - * @param delegatee The address to delegate votes to - */ - function delegate(address delegatee) external { - return _delegate(msg.sender, delegatee); - } - - /** - * @notice Delegates votes from signatory to `delegatee` - * @param delegatee The address to delegate votes to - * @param nonce The contract state required to match the signature - * @param expiry The time at which to expire the signature - * @param v The recovery byte of the signature - * @param r Half of the ECDSA signature pair - * @param s Half of the ECDSA signature pair - */ - function delegateBySig( - address delegatee, - uint nonce, - uint expiry, - uint8 v, - bytes32 r, - bytes32 s - ) - external - { - bytes32 domainSeparator = keccak256( - abi.encode( - DOMAIN_TYPEHASH, - keccak256(bytes(name())), - getChainId(), - address(this) - ) - ); - - bytes32 structHash = keccak256( - abi.encode( - DELEGATION_TYPEHASH, - delegatee, - nonce, - expiry - ) - ); - - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - domainSeparator, - structHash - ) - ); - - address signatory = ecrecover(digest, v, r, s); - require(signatory != address(0), "TULIP::delegateBySig: invalid signature"); - require(nonce == nonces[signatory]++, "TULIP::delegateBySig: invalid nonce"); - require(now <= expiry, "TULIP::delegateBySig: signature expired"); - return _delegate(signatory, delegatee); - } - - /** - * @notice Gets the current votes balance for `account` - * @param account The address to get votes balance - * @return The number of current votes for `account` - */ - function getCurrentVotes(address account) - external - view - returns (uint256) - { - uint32 nCheckpoints = numCheckpoints[account]; - return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; - } - - /** - * @notice Determine the prior number of votes for an account as of a block number - * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. - * @param account The address of the account to check - * @param blockNumber The block number to get the vote balance at - * @return The number of votes the account had as of the given block - */ - function getPriorVotes(address account, uint blockNumber) - external - view - returns (uint256) - { - require(blockNumber < block.number, "TULIP::getPriorVotes: not yet determined"); - - uint32 nCheckpoints = numCheckpoints[account]; - if (nCheckpoints == 0) { - return 0; - } - - // First check most recent balance - if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { - return checkpoints[account][nCheckpoints - 1].votes; - } - - // Next check implicit zero balance - if (checkpoints[account][0].fromBlock > blockNumber) { - return 0; - } - - uint32 lower = 0; - uint32 upper = nCheckpoints - 1; - while (upper > lower) { - uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow - Checkpoint memory cp = checkpoints[account][center]; - if (cp.fromBlock == blockNumber) { - return cp.votes; - } else if (cp.fromBlock < blockNumber) { - lower = center; - } else { - upper = center - 1; - } - } - return checkpoints[account][lower].votes; - } - - function _delegate(address delegator, address delegatee) - internal - { - address currentDelegate = _delegates[delegator]; - uint256 delegatorBalance = balanceOf(delegator); // balance of underlying TULIPs (not scaled); - _delegates[delegator] = delegatee; - - emit DelegateChanged(delegator, currentDelegate, delegatee); - - _moveDelegates(currentDelegate, delegatee, delegatorBalance); - } - - function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal { - if (srcRep != dstRep && amount > 0) { - if (srcRep != address(0)) { - // decrease old representative - uint32 srcRepNum = numCheckpoints[srcRep]; - uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; - uint256 srcRepNew = srcRepOld.sub(amount); - _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); - } - - if (dstRep != address(0)) { - // increase new representative - uint32 dstRepNum = numCheckpoints[dstRep]; - uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; - uint256 dstRepNew = dstRepOld.add(amount); - _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); - } - } - } - - function _writeCheckpoint( - address delegatee, - uint32 nCheckpoints, - uint256 oldVotes, - uint256 newVotes - ) - internal - { - uint32 blockNumber = safe32(block.number, "TULIP::_writeCheckpoint: block number exceeds 32 bits"); - - if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { - checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; - } else { - checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); - numCheckpoints[delegatee] = nCheckpoints + 1; - } - - emit DelegateVotesChanged(delegatee, oldVotes, newVotes); + /// @notice Burns `_amount` token to `_to`. Must only be called by the owner (MasterChef). + function burn(address _to, uint256 _amount) public onlyOwner { + _burn(_to, _amount); } - function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { - require(n < 2**32, errorMessage); - return uint32(n); + /// @dev Sets the account to whitelist and not whitelist + function setWhiteListAccount(address account, bool status) external onlyAdmin() { + whiteList[account] = status; + emit WhiteList(account, status); } - function getChainId() internal pure returns (uint) { - uint256 chainId; - assembly { chainId := chainid() } - return chainId; + /// @dev function to change the `admin` address, can only be done by the address + function changeAdmin(address _admin) public onlyAdmin() { + require(_admin != address(0), "Admin can't be 0x00000"); + admin = _admin; } -} \ No newline at end of file +} diff --git a/contracts/MochaTokenOLD.sol b/contracts/MochaTokenOLD.sol new file mode 100644 index 0000000..9c2b531 --- /dev/null +++ b/contracts/MochaTokenOLD.sol @@ -0,0 +1,382 @@ +pragma solidity ^0.6.0; + +import "./lib/IBEP20.sol"; +import "./lib/SafeMath.sol"; +import "./lib/Address.sol"; +import "./lib/Ownable.sol"; + + +/** + * @dev Implementation of the {IBEP20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {BEP20MinterPauser}. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of BEP20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IBEP20-approve}. + */ + +contract MochaToken is Context, IBEP20, Ownable { + using SafeMath for uint256; + using Address for address; + + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + /** + * Additions to the MOCHA token to have it burn and mint tokens on tx's + * + */ + /// @dev This address is will be used to send 0.1% of the MOCHA tokens to. + address public rewardAddress; + + /// @dev These are used to calcualte how much of the token is to be burned. + uint constant public BURN_FEE = 900; // 0.9 + uint constant public REWARD_FEE = 100; // 0.1 + uint constant public MAX_FEE = 100000; + uint256 constant public HARD_CAP = 500000e18; + address public admin; + + mapping (address => bool ) public whiteList; + mapping (address => uint256) private _balances; + mapping (address => mapping (address => uint256)) private _allowances; + + event WhiteList(address account, bool status); + event SetRewardAddress(address caller, address newRewardAddress); + + + modifier checkHardCap(uint256 amount) { + require(totalSupply().add(amount) <= HARD_CAP, "Supply is greater than HARD_CAP"); + _; + } + + modifier onlyAdmin(){ + require(admin == msg.sender, "caller is not the admin"); + _; + } + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (address _rewardAddress) public { + _name = "MochaToken"; + _symbol = "MOCHA"; + _decimals = 18; + admin = msg.sender; + rewardAddress = _rewardAddress; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {BEP20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IBEP20-balanceOf} and {IBEP20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IBEP20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IBEP20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IBEP20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IBEP20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {BEP20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance")); + return true; + } + + /** + * @dev See {IBEP20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IBEP20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IBEP20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IBEP20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "BEP20: decreased allowance below zero")); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "BEP20: transfer from the zero address"); + require(recipient != address(0), "BEP20: transfer to the zero address"); + + if(whiteList[sender] == true || whiteList[recipient] == true){ + _transferToken(sender, recipient, amount); + }else{ + (uint256 toBurn, uint256 toReward, uint256 receiverAmount) = calculateFees(amount); + + // NOTE POTENTIAL BUG HERE IT SHOULD BE BURNED FROM RECEIVER + _burn(sender, toBurn); + _transferToken(sender, rewardAddress, toReward); + _transferToken(sender, recipient, receiverAmount); + } + } + + function _transferToken(address sender, address recipient, uint256 amount) internal { + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "BEP20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "BEP20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "BEP20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "BEP20: approve from the zero address"); + require(spender != address(0), "BEP20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } + + + + /// @notice This is where the address of the reward pool is set. + function setRewardAddress(address _rewardAddress) public onlyOwner { + require(_rewardAddress != address(0x00),"zero address"); + rewardAddress = _rewardAddress; + emit SetRewardAddress(msg.sender,_rewardAddress); + } + + /// @notice Helper function to calcualte the fees which will be deducted from + /// the transaction + function calculateFees(uint256 amount) public pure returns (uint256, uint256, uint256) { + uint256 toBurn = amount.mul(BURN_FEE).div(MAX_FEE); + uint256 toReward = amount.mul(REWARD_FEE).div(MAX_FEE); + amount = amount.sub(toBurn+toReward); + return (toBurn, toReward, amount); + } + + /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterGardener). + function mint(address _to, uint256 _amount) public onlyOwner checkHardCap(_amount) { + _mint(_to, _amount); + } + + /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterGardener). + function burn(address _from, uint256 _amount) public onlyOwner returns(bool) { + _burn(_from, _amount); + return true; + } + + function whiteListAccount(address account, bool status) external onlyAdmin() { + whiteList[account] = status; + emit WhiteList(account, status); + } +} diff --git a/contracts/MockBEP20.sol b/contracts/MockBEP20.sol new file mode 100644 index 0000000..2203f69 --- /dev/null +++ b/contracts/MockBEP20.sol @@ -0,0 +1,966 @@ + +pragma solidity ^0.6.0; +import "./lib/SafeMath.sol"; +import "./lib/Address.sol"; +import "./lib/Ownable.sol"; + + +// File: @openzeppelin/contracts/token/ERC20/IERC20.sol + + +pragma solidity ^0.6.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + + + + +// File: @openzeppelin/contracts/token/ERC20/ERC20.sol + + +pragma solidity ^0.6.0; + + + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + using Address for address; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol) public { + _name = name; + _symbol = symbol; + _decimals = 18; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `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"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + 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); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} + + + +// File: contracts/MockBEP20.sol + +// File: @openzeppelin/contracts/GSN/Context.sol + + +pragma solidity ^0.6.12; + + + + + +// File: @openzeppelin/contracts/token/ERC20/ERC20.sol + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20Basic is Context, IERC20 , Ownable{ + using SafeMath for uint256; + using Address for address; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + + // A record of each accounts delegate + mapping (address => address) internal _delegates; + + /// @notice A checkpoint for marking number of votes from a given block + struct Checkpoint { + uint32 fromBlock; + uint256 votes; + } + + /// @notice A record of votes checkpoints for each account, by index + mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; + + /// @notice The number of checkpoints for each account + mapping (address => uint32) public numCheckpoints; + + /// @notice The EIP-712 typehash for the contract's domain + bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); + + /// @notice The EIP-712 typehash for the delegation struct used by the contract + bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + /// @notice A record of states for signing / validating signatures + mapping (address => uint) public nonces; + + /// @notice An event thats emitted when an account changes its delegate + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /// @notice An event thats emitted when a delegate account's vote balance changes + event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); + + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol) public { + _name = name; + _symbol = symbol; + _decimals = 18; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `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"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + _moveDelegates(_delegates[sender], _delegates[recipient], amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + _moveDelegates(address(0), _delegates[account], amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + + + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + 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); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + + function _delegate(address delegator, address delegatee) + internal + { + address currentDelegate = _delegates[delegator]; + uint256 delegatorBalance = balanceOf(delegator); // balance of underlying (not scaled); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveDelegates(currentDelegate, delegatee, delegatorBalance); + } + + function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal { + if (srcRep != dstRep && amount > 0) { + if (srcRep != address(0)) { + // decrease old representative + uint32 srcRepNum = numCheckpoints[srcRep]; + uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; + uint256 srcRepNew = srcRepOld.sub(amount); + _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); + } + + if (dstRep != address(0)) { + // increase new representative + uint32 dstRepNum = numCheckpoints[dstRep]; + uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; + uint256 dstRepNew = dstRepOld.add(amount); + _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); + } + } + } + + function _writeCheckpoint( + address delegatee, + uint32 nCheckpoints, + uint256 oldVotes, + uint256 newVotes + ) + internal + { + uint32 blockNumber = safe32(block.number, "_writeCheckpoint: block number exceeds 32 bits"); + + if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { + checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; + } else { + checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); + numCheckpoints[delegatee] = nCheckpoints + 1; + } + + emit DelegateVotesChanged(delegatee, oldVotes, newVotes); + } + + function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { + require(n < 2**32, errorMessage); + return uint32(n); + } + + function getChainId() internal pure returns (uint) { + uint256 chainId; + assembly { chainId := chainid() } + return chainId; + } + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} + + +// Token with Governance. +contract MockBEP20 is ERC20Basic { + + //Initialize Token name, symbol, decimal and totalsupply + constructor () public ERC20Basic ("MockBEP20", "MockBEP20") { + + } + + /** + * @dev Burns a specific amount of tokens. Can be executed by contract Operator only. + * @param _value The amount of token to be burned. + */ + + function burn(uint256 _value) public onlyOwner { + _burn(msg.sender,_value); + } + + function mint(address _account, uint256 _value) public onlyOwner returns(bool) { + _mint(_account, _value); + return true; + } + /** + * @notice Delegate votes from `msg.sender` to `delegatee` + * @param delegator The address to get delegatee for + */ + function delegates(address delegator) + external + view + returns (address) + { + return _delegates[delegator]; + } + + /** + * @notice Delegate votes from `msg.sender` to `delegatee` + * @param delegatee The address to delegate votes to + */ + function delegate(address delegatee) external { + return _delegate(msg.sender, delegatee); + } + + /** + * @notice Delegates votes from signatory to `delegatee` + * @param delegatee The address to delegate votes to + * @param nonce The contract state required to match the signature + * @param expiry The time at which to expire the signature + * @param v The recovery byte of the signature + * @param r Half of the ECDSA signature pair + * @param s Half of the ECDSA signature pair + */ + function delegateBySig( + address delegatee, + uint nonce, + uint expiry, + uint8 v, + bytes32 r, + bytes32 s + ) + external + + { + bytes32 domainSeparator = keccak256( + abi.encode( + DOMAIN_TYPEHASH, + keccak256(bytes(name())), + getChainId(), + address(this) + ) + ); + + bytes32 structHash = keccak256( + abi.encode( + DELEGATION_TYPEHASH, + delegatee, + nonce, + expiry + ) + ); + + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + structHash + ) + ); + + address signatory = ecrecover(digest, v, r, s); + require(signatory != address(0), "delegateBySig: invalid signature"); + require(nonce == nonces[signatory]++, "delegateBySig: invalid nonce"); + require(now <= expiry, "delegateBySig: signature expired"); + return _delegate(signatory, delegatee); + } + + /** + * @notice Gets the current votes balance for `account` + * @param account The address to get votes balance + * @return The number of current votes for `account` + */ + function getCurrentVotes(address account) + external + view + returns (uint256) + { + uint32 nCheckpoints = numCheckpoints[account]; + return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; + } + + /** + * @notice Determine the prior number of votes for an account as of a block number + * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. + * @param account The address of the account to check + * @param blockNumber The block number to get the vote balance at + * @return The number of votes the account had as of the given block + */ + function getPriorVotes(address account, uint blockNumber) + external + view + returns (uint256) + { + require(blockNumber < block.number, "getPriorVotes: not yet determined"); + + uint32 nCheckpoints = numCheckpoints[account]; + if (nCheckpoints == 0) { + return 0; + } + + // First check most recent balance + if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { + return checkpoints[account][nCheckpoints - 1].votes; + } + + // Next check implicit zero balance + if (checkpoints[account][0].fromBlock > blockNumber) { + return 0; + } + + uint32 lower = 0; + uint32 upper = nCheckpoints - 1; + while (upper > lower) { + uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow + Checkpoint memory cp = checkpoints[account][center]; + if (cp.fromBlock == blockNumber) { + return cp.votes; + } else if (cp.fromBlock < blockNumber) { + lower = center; + } else { + upper = center - 1; + } + } + return checkpoints[account][lower].votes; + } + + +} diff --git a/contracts/lib/Address.sol b/contracts/lib/Address.sol new file mode 100644 index 0000000..75e2cf6 --- /dev/null +++ b/contracts/lib/Address.sol @@ -0,0 +1,159 @@ +pragma solidity ^0.6.2; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { + codehash := extcodehash(account) + } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{value: amount}(''); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, 'Address: low-level call failed'); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return _functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, 'Address: low-level call with value failed'); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + return _functionCallWithValue(target, data, value, errorMessage); + } + + function _functionCallWithValue( + address target, + bytes memory data, + uint256 weiValue, + string memory errorMessage + ) private returns (bytes memory) { + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{value: weiValue}(data); + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/contracts/lib/Context.sol b/contracts/lib/Context.sol new file mode 100644 index 0000000..8f5968a --- /dev/null +++ b/contracts/lib/Context.sol @@ -0,0 +1,24 @@ +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +contract Context { + // Empty internal constructor, to prevent people from mistakenly deploying + // an instance of this contract, which should be used via inheritance. + constructor () internal { } + + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode + return msg.data; + } +} \ No newline at end of file diff --git a/contracts/lib/IBEP20.sol b/contracts/lib/IBEP20.sol new file mode 100644 index 0000000..8435c62 --- /dev/null +++ b/contracts/lib/IBEP20.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.6.0; + +/** + * @dev Interface of the BEP20 standard as defined in the EIP. + */ +interface IBEP20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} \ No newline at end of file diff --git a/contracts/lib/Ownable.sol b/contracts/lib/Ownable.sol new file mode 100644 index 0000000..9b28c38 --- /dev/null +++ b/contracts/lib/Ownable.sol @@ -0,0 +1,68 @@ +pragma solidity ^0.6.0; +import "./Context.sol"; + + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor () internal { + address msgSender = _msgSender(); + _owner = msgSender; + emit OwnershipTransferred(address(0), msgSender); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(_owner == _msgSender(), "Ownable: caller is not the owner"); + _; + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + emit OwnershipTransferred(_owner, address(0)); + _owner = address(0); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + emit OwnershipTransferred(_owner, newOwner); + _owner = newOwner; + } +} + diff --git a/contracts/lib/ReentrancyGuard.sol b/contracts/lib/ReentrancyGuard.sol new file mode 100644 index 0000000..7022d22 --- /dev/null +++ b/contracts/lib/ReentrancyGuard.sol @@ -0,0 +1,60 @@ +pragma solidity 0.6.12; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor () internal { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and make it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + // On the first call to nonReentrant, _notEntered will be true + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + + _; + + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } +} \ No newline at end of file diff --git a/contracts/lib/SafeBEP20.sol b/contracts/lib/SafeBEP20.sol new file mode 100644 index 0000000..051c158 --- /dev/null +++ b/contracts/lib/SafeBEP20.sol @@ -0,0 +1,100 @@ +pragma solidity 0.6.12; +import "./SafeMath.sol"; +import "./Address.sol"; +import "./IBEP20.sol"; + + + +/** + * @title SafeBEP20 + * @dev Wrappers around BEP20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeBEP20 for IBEP20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeBEP20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer( + IBEP20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IBEP20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IBEP20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IBEP20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeBEP20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance( + IBEP20 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( + IBEP20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub( + value, + "SafeBEP20: decreased allowance below zero" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IBEP20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, 'SafeBEP20: low-level call failed'); + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeBEP20: BEP20 operation did not succeed"); + } + } +} diff --git a/contracts/lib/SafeMath.sol b/contracts/lib/SafeMath.sol new file mode 100644 index 0000000..8143808 --- /dev/null +++ b/contracts/lib/SafeMath.sol @@ -0,0 +1,150 @@ +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, "SafeMath: subtraction overflow"); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, "SafeMath: division by zero"); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return mod(a, b, "SafeMath: modulo by zero"); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} \ No newline at end of file diff --git a/contracts/flat.sol b/flat/MochaToken.sol similarity index 75% rename from contracts/flat.sol rename to flat/MochaToken.sol index 60c3609..da3493e 100644 --- a/contracts/flat.sol +++ b/flat/MochaToken.sol @@ -710,35 +710,75 @@ contract MochaToken is ERC20("MochaToken", "MOCHA"), Ownable { */ /// @dev This address is will be used to send 0.1% of the MOCHA tokens to. address public rewardAddress; - + /** + * @dev admin address is used to take care of the whitelisting process once + * ownership is transfered to MasterChef + */ + address public admin; + + /** + * @dev burn address is where the tokens are going to be sent to instead + * of being burned. This makes it easier for users to keep track of. + * This isn't changeable for trust and security reasons. + */ + address public burnAddress = 0x000000000000000000000000000000000000dEaD; + /// @dev These are used to calcualte how much of the token is to be burned. uint constant public BURN_FEE = 900; uint constant public REWARD_FEE = 100; uint constant public MAX_FEE = 100000; - uint256 constant public HARD_CAP = 10 ** 7 * 1e18; + uint256 constant public HARD_CAP = 5 * 10 ** 5 * 1e18; + /// @dev List of whitelisted addresses + mapping (address => bool) public whiteList; + + event WhiteList(address account, bool status); + event SetRewardAddress(address caller, address newRewardAddress); + + // Check which makes sure that the HARD_CAP wasn't reached modifier checkHardCap(uint256 amount) { require(totalSupply().add(amount) <= HARD_CAP, "Supply is greater than 10 mil"); _; } + // Check to make sure only the admin can access the WhiteList function + modifier onlyAdmin(){ + require(admin == msg.sender, "Caller not Admin!"); + _; + } + + /** + * @dev Sets the values for {rewardAddress} which is the address of + * of the reward pool. It's best to set the {admin} address manually + * just incase a deployer contract is used. + */ + constructor (address _rewardAddress, address _admin) public { + rewardAddress = _rewardAddress; + admin = _admin; + } /// @notice This is where the address of the reward pool is set. function setRewardAddress(address _rewardAddress) public onlyOwner { rewardAddress = _rewardAddress; + emit SetRewardAddress(msg.sender,_rewardAddress); } - - function _transfer(address sender, address recipient, uint256 amount) internal virtual override { - super._transfer(sender, recipient, amount); - (uint256 toBurn, uint256 toReward) = calculateFees(amount); - - _burn(recipient, toBurn.add(toReward)); - _mint(rewardAddress, toReward); - emit Transfer(sender, recipient, amount); + /** + * @dev Custom transfer function to burn tokens from users who are not + * white listed. If a user is white listed a normal transfer occurs. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual override { + // We want to burn tokens if both the sender/recipient aren't whitelisted + if(whiteList[sender] == false && whiteList[recipient] == false) { + (uint256 toBurn, uint256 toReward) = calculateFees(amount); + super._transfer(sender, recipient, amount.sub(toBurn.add(toReward))); + super._transfer(sender, burnAddress, toBurn); + super._transfer(sender, rewardAddress, toReward); + } else { + super._transfer(sender, recipient, amount); + } } - - + /// @notice Helper function to calcualte the fees which will be deducted from /// the transaction function calculateFees(uint256 amount) public pure returns (uint256, uint256) { @@ -750,236 +790,17 @@ contract MochaToken is ERC20("MochaToken", "MOCHA"), Ownable { /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterGardener). function mint(address _to, uint256 _amount) public onlyOwner checkHardCap(_amount) { _mint(_to, _amount); - _moveDelegates(address(0), _delegates[_to], _amount); - } - - // Copied and modified from YAM code: - // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernanceStorage.sol - // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernance.sol - // Which is copied and modified from COMPOUND: - // https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol - - /// @notice A record of each accounts delegate - mapping (address => address) internal _delegates; - - /// @notice A checkpoint for marking number of votes from a given block - struct Checkpoint { - uint32 fromBlock; - uint256 votes; - } - - /// @notice A record of votes checkpoints for each account, by index - mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; - - /// @notice The number of checkpoints for each account - mapping (address => uint32) public numCheckpoints; - - /// @notice The EIP-712 typehash for the contract's domain - bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); - - /// @notice The EIP-712 typehash for the delegation struct used by the contract - bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - - /// @notice A record of states for signing / validating signatures - mapping (address => uint) public nonces; - - /// @notice An event thats emitted when an account changes its delegate - event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); - - /// @notice An event thats emitted when a delegate account's vote balance changes - event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); - - /** - * @notice Delegate votes from `msg.sender` to `delegatee` - * @param delegator The address to get delegatee for - */ - function delegates(address delegator) - external - view - returns (address) - { - return _delegates[delegator]; - } - - /** - * @notice Delegate votes from `msg.sender` to `delegatee` - * @param delegatee The address to delegate votes to - */ - function delegate(address delegatee) external { - return _delegate(msg.sender, delegatee); - } - - /** - * @notice Delegates votes from signatory to `delegatee` - * @param delegatee The address to delegate votes to - * @param nonce The contract state required to match the signature - * @param expiry The time at which to expire the signature - * @param v The recovery byte of the signature - * @param r Half of the ECDSA signature pair - * @param s Half of the ECDSA signature pair - */ - function delegateBySig( - address delegatee, - uint nonce, - uint expiry, - uint8 v, - bytes32 r, - bytes32 s - ) - external - { - bytes32 domainSeparator = keccak256( - abi.encode( - DOMAIN_TYPEHASH, - keccak256(bytes(name())), - getChainId(), - address(this) - ) - ); - - bytes32 structHash = keccak256( - abi.encode( - DELEGATION_TYPEHASH, - delegatee, - nonce, - expiry - ) - ); - - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - domainSeparator, - structHash - ) - ); - - address signatory = ecrecover(digest, v, r, s); - require(signatory != address(0), "TULIP::delegateBySig: invalid signature"); - require(nonce == nonces[signatory]++, "TULIP::delegateBySig: invalid nonce"); - require(now <= expiry, "TULIP::delegateBySig: signature expired"); - return _delegate(signatory, delegatee); - } - - /** - * @notice Gets the current votes balance for `account` - * @param account The address to get votes balance - * @return The number of current votes for `account` - */ - function getCurrentVotes(address account) - external - view - returns (uint256) - { - uint32 nCheckpoints = numCheckpoints[account]; - return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; - } - - /** - * @notice Determine the prior number of votes for an account as of a block number - * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. - * @param account The address of the account to check - * @param blockNumber The block number to get the vote balance at - * @return The number of votes the account had as of the given block - */ - function getPriorVotes(address account, uint blockNumber) - external - view - returns (uint256) - { - require(blockNumber < block.number, "TULIP::getPriorVotes: not yet determined"); - - uint32 nCheckpoints = numCheckpoints[account]; - if (nCheckpoints == 0) { - return 0; - } - - // First check most recent balance - if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { - return checkpoints[account][nCheckpoints - 1].votes; - } - - // Next check implicit zero balance - if (checkpoints[account][0].fromBlock > blockNumber) { - return 0; - } - - uint32 lower = 0; - uint32 upper = nCheckpoints - 1; - while (upper > lower) { - uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow - Checkpoint memory cp = checkpoints[account][center]; - if (cp.fromBlock == blockNumber) { - return cp.votes; - } else if (cp.fromBlock < blockNumber) { - lower = center; - } else { - upper = center - 1; - } - } - return checkpoints[account][lower].votes; - } - - function _delegate(address delegator, address delegatee) - internal - { - address currentDelegate = _delegates[delegator]; - uint256 delegatorBalance = balanceOf(delegator); // balance of underlying TULIPs (not scaled); - _delegates[delegator] = delegatee; - - emit DelegateChanged(delegator, currentDelegate, delegatee); - - _moveDelegates(currentDelegate, delegatee, delegatorBalance); - } - - function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal { - if (srcRep != dstRep && amount > 0) { - if (srcRep != address(0)) { - // decrease old representative - uint32 srcRepNum = numCheckpoints[srcRep]; - uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; - uint256 srcRepNew = srcRepOld.sub(amount); - _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); - } - - if (dstRep != address(0)) { - // increase new representative - uint32 dstRepNum = numCheckpoints[dstRep]; - uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; - uint256 dstRepNew = dstRepOld.add(amount); - _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); - } - } - } - - function _writeCheckpoint( - address delegatee, - uint32 nCheckpoints, - uint256 oldVotes, - uint256 newVotes - ) - internal - { - uint32 blockNumber = safe32(block.number, "TULIP::_writeCheckpoint: block number exceeds 32 bits"); - - if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { - checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; - } else { - checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); - numCheckpoints[delegatee] = nCheckpoints + 1; - } - - emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } - function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { - require(n < 2**32, errorMessage); - return uint32(n); + /// @dev Sets the account to whitelist and not whitelist + function setWhiteListAccount(address account, bool status) external onlyAdmin() { + whiteList[account] = status; + emit WhiteList(account, status); } - function getChainId() internal pure returns (uint) { - uint256 chainId; - assembly { chainId := chainid() } - return chainId; + /// @dev function to change the `admin` address, can only be done by the address + function changeAdmin(address _admin) public onlyAdmin() { + require(admin != address(0), "Admin can't be 0x00000"); + admin = _admin; } } diff --git a/test/masterChef.test.js b/test/masterChef.test.js new file mode 100644 index 0000000..d0407f3 --- /dev/null +++ b/test/masterChef.test.js @@ -0,0 +1,266 @@ +const { expectRevert, time } = require('@openzeppelin/test-helpers'); +const masterChefV2 = artifacts.require('MasterChefV2'); + +const MochaToken = artifacts.require('MochaToken'); +const Mocktoken = artifacts.require('MockBEP20'); + + + +contract('masterChefv2', ([alice, bob, carol, dev, minter]) => { + + context('masterChef test', () => { + beforeEach(async () => { + + this.mocha = await MochaToken.new(dev,{ from: minter }); + + this.lp1 = await Mocktoken.new({from: minter}); + this.lp2 = await Mocktoken.new({from: minter}); + + await this.lp1.mint(alice,web3.utils.toWei('50'),{from: minter}) + await this.lp1.mint(bob,web3.utils.toWei('50'),{from: minter}) + await this.lp1.mint(carol,web3.utils.toWei('50'),{from: minter}) + + await this.lp2.mint(alice,web3.utils.toWei('50'),{from: minter}) + await this.lp2.mint(bob,web3.utils.toWei('50'),{from: minter}) + await this.lp2.mint(carol,web3.utils.toWei('50'),{from: minter}) + + }); + + it('should give out mochas only after farming time', async () => { + // 1000 per block farming rate starting at block 100 + + this.masterChef = await masterChefV2.new(this.mocha.address,minter,minter,'1000','100',{from: minter}); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address,{from:minter}); + + await this.masterChef.add('100', this.lp1.address, 0,true,{from: minter}); + await this.lp1.approve(this.masterChef.address, '1000', { from: bob }); + await this.masterChef.deposit(0, '100', { from: bob }); + await time.advanceBlockTo('89'); + await this.masterChef.deposit(0, '0', { from: bob }); // block 90 + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '0'); + await time.advanceBlockTo('94'); + await this.masterChef.deposit(0, '0', { from: bob }); // block 95 + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '0'); + await time.advanceBlockTo('99'); + await this.masterChef.deposit(0, '0', { from: bob }); // block 100 + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '0'); + await time.advanceBlockTo('100'); + await this.masterChef.deposit(0, '0', { from: bob }); // block 101 + // console.log("bob Balance",Number(await this.mocha.balanceOf(bob))) + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '855'); + await time.advanceBlockTo('104'); + await this.masterChef.deposit(0, '0', { from: bob }); // block 105 + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '4275'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), '500'); + assert.equal((await this.mocha.totalSupply()).valueOf(), '4775'); + }); + + it('should give proper mochas allocation to each pool and deduct 5% fee on rewards distribution ', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address,minter,minter,'1000','200',{from: minter}); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + await this.lp1.approve(this.masterChef.address, '1000', { from: alice }); + await this.lp2.approve(this.masterChef.address, '1000', { from: bob }); + // Add first LP to the pool with allocation 1 + await this.masterChef.add('10', this.lp1.address, 0,true, { from: minter }); + // Alice deposits 10 LPs at block 410 + await time.advanceBlockTo('209'); + await this.masterChef.deposit(0, '10', { from: alice }); + // Add LP2 to the pool with allocation 2 at block 420 + await time.advanceBlockTo('219'); + await this.masterChef.add('20', this.lp2.address, 0,true, { from: minter }); + // Alice should have 10*1000 pending reward + assert.equal((await this.masterChef.pendingBrew(0, alice)).valueOf(), '9000'); + // Bob deposits 10 LP2s at block 425 + await time.advanceBlockTo('224'); + await this.masterChef.deposit(1, '5', { from: bob }); + assert.equal((await this.masterChef.pendingBrew(0, alice)).valueOf(), '10500'); + await time.advanceBlockTo('230'); + // At block 430. Bob should get 5*2/3*900. + assert.equal((await this.masterChef.pendingBrew(1, bob)).valueOf(), '3000'); + }); + + it('should distribute mochas properly for each staker', async () =>{ + // 100 per block farming rate starting at block 300 with bonus until block 1000 + this.masterChef = await masterChefV2.new(this.mocha.address, minter, minter,'1000', '300', { from: minter }); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + await this.masterChef.add('100', this.lp1.address, '0',true,{ from: minter }); + await this.lp1.approve(this.masterChef.address, '1000', { from: alice }); + await this.lp1.approve(this.masterChef.address, '1000', { from: bob }); + await this.lp1.approve(this.masterChef.address, '1000', { from: carol }); + // Alice deposits 10 LPs at block 310 + await time.advanceBlockTo('309'); + await this.masterChef.deposit(0, '10', { from: alice }); + // Bob deposits 20 LPs at block 314 + await time.advanceBlockTo('313'); + await this.masterChef.deposit(0, '20', { from: bob }); + // Carol deposits 30 LPs at block 318 + await time.advanceBlockTo('317'); + await this.masterChef.deposit(0, '30', { from: carol }); + // Alice deposits 10 more LPs at block 320. At this point: + // Alice should have: 4*900 + 4*1/3*900 + 2*1/6*900 + await time.advanceBlockTo('319') + await this.masterChef.deposit(0, '10', { from: alice }); + assert.equal((await this.masterChef.totalSupply()).valueOf(), '10000'); + assert.equal((await this.mocha.balanceOf(alice)).valueOf(), '4845'); + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(carol)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), '1000'); + + await time.advanceBlockTo('329') + await this.masterChef.withdraw(0, '5', { from: bob }); + // console.log(Number(await this.masterChef.totalSupply())) + assert.equal((await this.masterChef.totalSupply()).valueOf(), '20000'); + assert.equal((await this.mocha.balanceOf(alice)).valueOf(), '4845'); + assert.equal((await this.mocha.balanceOf(carol)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), '2000'); + + }); + + it('should distribute only 460k mochas after it brewPerBlock becomes 0(no reward distribution)', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, minter, minter,web3.utils.toWei('23000'), '400', { from: minter }); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + await this.masterChef.add('100', this.lp1.address, '0',true,{ from: minter }); + await this.lp1.approve(this.masterChef.address, '1000', { from: alice }); + await this.lp1.approve(this.masterChef.address, '1000', { from: bob }); + await this.lp1.approve(this.masterChef.address, '1000', { from: carol }); + // Alice deposits 10 LPs at block 310 + await time.advanceBlockTo('409'); + await this.masterChef.deposit(0, '10', { from: alice }); + // Bob deposits 20 LPs at block 314 + await time.advanceBlockTo('413'); + await this.masterChef.deposit(0, '20', { from: bob }); + // Carol deposits 30 LPs at block 318 + await time.advanceBlockTo('417'); + await this.masterChef.deposit(0, '30', { from: carol }); + // Alice deposits 10 more LPs at block 320. At this point: + // Alice should have: (4*23000 + 4*1/3*23000 + 2*1/6*23000)*10/100 + await time.advanceBlockTo('419') + await this.masterChef.deposit(0, '10', { from: alice }); 111435 + assert.equal((await this.masterChef.totalSupply()).valueOf(), web3.utils.toWei('230000')); + assert.equal((await this.mocha.balanceOf(alice)).valueOf(), web3.utils.toWei('111435')); + assert.equal((await this.mocha.balanceOf(bob)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(carol)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), web3.utils.toWei('23000')); + + await time.advanceBlockTo('429') + await this.masterChef.withdraw(0, '5', { from: bob }); + assert.equal((await this.masterChef.totalSupply()).valueOf(), web3.utils.toWei('460000')); + assert.equal((await this.mocha.balanceOf(carol)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), web3.utils.toWei('46000')); + + assert.equal((await this.masterChef.brewPerBlock()).valueOf(), '0'); + + await time.advanceBlockTo('459') + await this.masterChef.withdraw(0, '5', { from: bob }); + assert.equal((await this.masterChef.totalSupply()).valueOf(), web3.utils.toWei('460000')); + assert.equal((await this.mocha.balanceOf(carol)).valueOf(), '0'); + assert.equal((await this.mocha.balanceOf(minter)).valueOf(), web3.utils.toWei('46000')); + + }); + + it('should allow emergency withdraw', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, minter,minter, '100', '100', { from: alice }); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + await this.masterChef.add('100', this.lp1.address, 0,true); + await this.lp1.approve(this.masterChef.address, web3.utils.toWei('50'), { from: bob }); + await this.masterChef.deposit(0, web3.utils.toWei('40'), { from: bob }); + assert.equal((await this.lp1.balanceOf(bob)).valueOf(), web3.utils.toWei('10')); + await this.masterChef.emergencyWithdraw(0, { from: bob }); + assert.equal((await this.lp1.balanceOf(bob)).valueOf(), web3.utils.toWei('50')); + }); + + it('should deduct deposit fee on each deposit ', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, minter,minter, '100', '100', { from: alice }); + await this.mocha.whiteListAccount(this.masterChef.address,true,{from:minter}) + + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + await this.masterChef.add('100', this.lp1.address, 500,true); + await this.lp1.approve(this.masterChef.address, web3.utils.toWei('60'), { from: bob }); + await this.masterChef.deposit(0, web3.utils.toWei('40'), { from: bob }); + + assert.equal((await this.lp1.balanceOf(bob)).valueOf(), web3.utils.toWei('10')); + await this.masterChef.deposit(0, web3.utils.toWei('10'), { from: bob }); + + await this.masterChef.emergencyWithdraw(0, { from: bob }); + assert.equal((await this.lp1.balanceOf(bob)).valueOf(), web3.utils.toWei('47.5')); + }); + + it('should set correct state variables', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,dev, '1000', '1000', { from: alice }); + await this.mocha.transferOwnership(this.masterChef.address, { from: minter }); + const brew = await this.masterChef.brew(); + const devaddr = await this.masterChef.devaddr(); + const owner = await this.mocha.owner(); + assert.equal(brew.valueOf(), this.mocha.address); + assert.equal(devaddr.valueOf(), dev); + assert.equal(owner.valueOf(), this.masterChef.address); + }); + + it('should allow dev and only dev to update dev', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,dev, '1000', '1000', { from: alice }); + assert.equal((await this.masterChef.devaddr()).valueOf(), dev); + await expectRevert(this.masterChef.dev(bob, { from: bob }), 'dev: wut?'); + await this.masterChef.dev(bob, { from: dev }); + assert.equal((await this.masterChef.devaddr()).valueOf(), bob); + await this.masterChef.dev(alice, { from: bob }); + assert.equal((await this.masterChef.devaddr()).valueOf(), alice); + }) + + it('should allow feeAddress and only feeAddress to update feeAddress', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,minter, '1000', '1000', { from: alice }); + assert.equal((await this.masterChef.feeAddress()).valueOf(), minter); + await expectRevert(this.masterChef.setFeeAddress(bob, { from: bob }), "setFeeAddress: FORBIDDEN"); + await this.masterChef.setFeeAddress(bob, { from: minter }); + assert.equal((await this.masterChef.feeAddress()).valueOf(), bob); + await this.masterChef.setFeeAddress(alice, { from: bob }); + assert.equal((await this.masterChef.feeAddress()).valueOf(), alice); + }) + + it('should allow owner and only owner to update updateEmissionRate revert if emissionRate is more than 1', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,minter, '1000', '1000', { from: alice }); + assert.equal((await this.masterChef.brewPerBlock()).valueOf(), '1000'); + await expectRevert(this.masterChef.updateEmissionRate("100", { from: bob }), "Ownable: caller is not the owner."); + await this.masterChef.updateEmissionRate('900', { from: alice }); + assert.equal((await this.masterChef.brewPerBlock()).valueOf(), '900'); + await expectRevert(this.masterChef.updateEmissionRate(web3.utils.toWei('1.1'), { from: alice }), "invalid brewPerBlock"); + + }) + + it('should allow owner and only owner to update poolInfo and revert for non existing pool', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,minter, '1000', '1000', { from: alice }); + await expectRevert(this.masterChef.add('100', this.lp1.address, 0,true, { from: bob }), "Ownable: caller is not the owner"); + await expectRevert(this.masterChef.add('100', this.lp1.address, 600,true, { from: alice }), "add: invalid deposit fee basis points"); + + await this.masterChef.add('100', this.lp1.address, 0,true); + await expectRevert(this.masterChef.add('100', this.lp1.address, 0,true, { from: alice }), "nonDuplicated: duplicated"); + await expectRevert(this.masterChef.set(1,'100', 0,true, { from: alice }), "isPoolExist: pool not exist"); + await expectRevert(this.masterChef.set(0,'100', 600,true, { from: alice }), "set: invalid deposit fee basis points"); + + await this.masterChef.set(0,'1000',0,true); + + }) + + it('should allow setBurnFee and only owner to update setBurnFee and revert if fee is more than 5%', async () => { + this.masterChef = await masterChefV2.new(this.mocha.address, dev,minter, '1000', '1000', { from: alice }); + assert.equal((await this.masterChef.burnFee()).valueOf(), '500'); + await expectRevert(this.masterChef.setBurnFee('300', { from: bob }), "Ownable: caller is not the owner"); + await this.masterChef.setBurnFee('300', { from: alice }); + assert.equal((await this.masterChef.burnFee()).valueOf(), '300'); + await expectRevert(this.masterChef.setBurnFee('600', { from: alice }), "setBurnFee: invalid burnFee fee basis points"); + + + }) + + + }); +}); diff --git a/test/mochaToken.test.js b/test/mochaToken.test.js new file mode 100644 index 0000000..b9a22ee --- /dev/null +++ b/test/mochaToken.test.js @@ -0,0 +1,198 @@ +const { expectRevert, ZERO_ADDRESS } = require('@openzeppelin/test-helpers'); +const MochaToken = artifacts.require('MochaToken'); + +contract('MochaToken', ([alice, bob, carol, fee]) => { + beforeEach(async () => { + this.Mocha = await MochaToken.new(fee, { from: alice }); + }); + + it('should have correct name and symbol and decimal', async () => { + const name = await this.Mocha.name(); + const symbol = await this.Mocha.symbol(); + const decimals = await this.Mocha.decimals(); + assert.equal(name.valueOf(), 'MochaToken'); + assert.equal(symbol.valueOf(), 'MOCHA'); + assert.equal(decimals.valueOf(), '18'); + }); + + it('should only allow owner to mint token', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await this.Mocha.mint(bob, web3.utils.toWei('1000'), { from: alice }); + await expectRevert( + this.Mocha.mint(carol, web3.utils.toWei('1000'), { from: bob }), + 'Ownable: caller is not the owner', + ); + const totalSupply = await this.Mocha.totalSupply(); + const aliceBal = await this.Mocha.balanceOf(alice); + const bobBal = await this.Mocha.balanceOf(bob); + const carolBal = await this.Mocha.balanceOf(carol); + assert.equal(totalSupply.valueOf(), web3.utils.toWei('1100')); + assert.equal(aliceBal.valueOf(), web3.utils.toWei('100')); + assert.equal(bobBal.valueOf(), web3.utils.toWei('1000')); + assert.equal(carolBal.valueOf(), '0'); + }); + + it('should supply token transfers properly with fee deduction and is feeAddress 0.1% token on each transfers ', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await this.Mocha.mint(bob, web3.utils.toWei('200'), { from: alice }); + await this.Mocha.transfer(carol, web3.utils.toWei('10'), { from: alice }); + await this.Mocha.transfer(carol, web3.utils.toWei('100'), { from: bob }); + const totalSupply = await this.Mocha.totalSupply(); + const aliceBal = await this.Mocha.balanceOf(alice); + const bobBal = await this.Mocha.balanceOf(bob); + const carolBal = await this.Mocha.balanceOf(carol); + const FeeBal = await this.Mocha.balanceOf(fee); + + // console.log("totalSupply",web3.utils.fromWei((totalSupply).toString())) + assert.equal(web3.utils.fromWei((totalSupply).toString()), '299.01'); + assert.equal(web3.utils.fromWei(aliceBal.toString()), '90'); + assert.equal(web3.utils.fromWei(bobBal.toString()), '100'); + assert.equal(web3.utils.fromWei(carolBal.toString()), '108.9'); + assert.equal(web3.utils.fromWei(FeeBal.toString()), '0.11'); + }); + + it('should supply token transfers properly with fee deduction and is feeAddress 0.1% token on each transfers ', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await this.Mocha.mint(bob, web3.utils.toWei('200'), { from: alice }); + + await this.Mocha.approve(carol, web3.utils.toWei('10'), { from: alice }); + await this.Mocha.approve(carol, web3.utils.toWei('100'), { from: bob }); + + await this.Mocha.transferFrom(alice,carol, web3.utils.toWei('10'), { from: carol }); + await this.Mocha.transferFrom(bob,carol, web3.utils.toWei('100'), { from: carol }); + + const totalSupply = await this.Mocha.totalSupply(); + const aliceBal = await this.Mocha.balanceOf(alice); + const bobBal = await this.Mocha.balanceOf(bob); + const carolBal = await this.Mocha.balanceOf(carol); + const FeeBal = await this.Mocha.balanceOf(fee); + + // console.log("totalSupply",web3.utils.fromWei((totalSupply).toString())) + assert.equal(web3.utils.fromWei((totalSupply).toString()), '299.01'); + assert.equal(web3.utils.fromWei(aliceBal.toString()), '90'); + assert.equal(web3.utils.fromWei(bobBal.toString()), '100'); + assert.equal(web3.utils.fromWei(carolBal.toString()), '108.9'); + assert.equal(web3.utils.fromWei(FeeBal.toString()), '0.11'); + }); + it('should not mint more than 500k ', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await this.Mocha.mint(bob, web3.utils.toWei('200'), { from: alice }); + + await this.Mocha.approve(carol, web3.utils.toWei('10'), { from: alice }); + await this.Mocha.approve(carol, web3.utils.toWei('100'), { from: bob }); + + await this.Mocha.transferFrom(alice,carol, web3.utils.toWei('10'), { from: carol }); + await this.Mocha.transferFrom(bob,carol, web3.utils.toWei('100'), { from: carol }); + + const totalSupply = await this.Mocha.totalSupply(); + const aliceBal = await this.Mocha.balanceOf(alice); + const bobBal = await this.Mocha.balanceOf(bob); + const carolBal = await this.Mocha.balanceOf(carol); + const FeeBal = await this.Mocha.balanceOf(fee); + + // console.log("totalSupply",web3.utils.fromWei((totalSupply).toString())) + assert.equal(web3.utils.fromWei((totalSupply).toString()), '299.01'); + assert.equal(web3.utils.fromWei(aliceBal.toString()), '90'); + assert.equal(web3.utils.fromWei(bobBal.toString()), '100'); + assert.equal(web3.utils.fromWei(carolBal.toString()), '108.9'); + assert.equal(web3.utils.fromWei(FeeBal.toString()), '0.11'); + + + await expectRevert( + this.Mocha.mint(bob, web3.utils.toWei('500000'), { from: alice }), + 'Supply is greater than HARD_CAP', + ); + await this.Mocha.mint(bob, web3.utils.toWei('499700'), { from: alice }); + + await expectRevert( + this.Mocha.mint(bob, web3.utils.toWei('1'), { from: alice }), + 'Supply is greater than HARD_CAP', + ); + + await expectRevert( + this.Mocha.mint(bob, web3.utils.toWei('0'), { from: bob }), + 'Ownable: caller is not the owner', + ); + await expectRevert( + this.Mocha.burn(bob, web3.utils.toWei('0'), { from: bob }), + 'Ownable: caller is not the owner', + ); + + await expectRevert( + this.Mocha.whiteListAccount(bob, 'true', { from: bob }), + 'caller is not the admin', + ); + + await expectRevert( + this.Mocha.setRewardAddress(bob, { from: bob }), + 'Ownable: caller is not the owner', + ); + + + await expectRevert( + this.Mocha.setRewardAddress("0x0000000000000000000000000000000000000000", { from: alice }), + 'zero address', + ); + + await this.Mocha.transferOwnership(carol,{from:alice}) + + await expectRevert( + this.Mocha.whiteListAccount(bob, 1, { from: carol }), + 'caller is not the admin', + ); + + await this.Mocha.whiteListAccount(alice,true,{from:alice}) + + + + }); + + it('should supply token transfers properly with no fee deduction if sender or receiver address is whiteList ', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await this.Mocha.mint(bob, web3.utils.toWei('200'), { from: alice }); + await this.Mocha.transferOwnership(carol,{from:alice}) + + await this.Mocha.approve(carol, web3.utils.toWei('10'), { from: alice }); + await this.Mocha.approve(carol, web3.utils.toWei('100'), { from: bob }); + + await this.Mocha.whiteListAccount(alice,true,{from:alice}) + await this.Mocha.transferFrom(alice,carol, web3.utils.toWei('10'), { from: carol }); + await this.Mocha.whiteListAccount(carol,true,{from:alice}) + await this.Mocha.transferFrom(bob,carol, web3.utils.toWei('100'), { from: carol }); + await this.Mocha.whiteListAccount(carol,false,{from:alice}) + + + const totalSupply = await this.Mocha.totalSupply(); + const aliceBal = await this.Mocha.balanceOf(alice); + const bobBal = await this.Mocha.balanceOf(bob); + const carolBal = await this.Mocha.balanceOf(carol); + const FeeBal = await this.Mocha.balanceOf(fee); + + // console.log("totalSupply",web3.utils.fromWei((totalSupply).toString())) + assert.equal(web3.utils.fromWei((totalSupply).toString()), '300'); + assert.equal(web3.utils.fromWei(aliceBal.toString()), '90'); + assert.equal(web3.utils.fromWei(bobBal.toString()), '100'); + assert.equal(web3.utils.fromWei(carolBal.toString()), '110'); + assert.equal(web3.utils.fromWei(FeeBal.toString()), '0'); + + await this.Mocha.transfer(carol, web3.utils.toWei('1'), { from: bob }); + assert.equal(web3.utils.fromWei((await this.Mocha.balanceOf(carol)).toString()), '110.99'); + assert.equal(web3.utils.fromWei((await this.Mocha.balanceOf(fee)).toString()), '0.001'); + assert.equal(web3.utils.fromWei((await this.Mocha.totalSupply()).toString()), '299.991'); + + + }); + + + it('should fail if you try to do bad transfers', async () => { + await this.Mocha.mint(alice, web3.utils.toWei('100'), { from: alice }); + await expectRevert( + this.Mocha.transfer(carol, web3.utils.toWei('110'), { from: alice }), + 'BEP20: transfer amount exceeds balance', + ); + await expectRevert( + this.Mocha.transfer(carol, '1', { from: bob }), + 'BEP20: transfer amount exceeds balance', + ); + }); + }); diff --git a/truffle-config.js b/truffle-config.js index a707377..3326a18 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -82,7 +82,7 @@ module.exports = { // Configure your compilers compilers: { solc: { - // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) + version: "0.6.12", // Fetch exact version from solc-bin (default: truffle's version) // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) // settings: { // See the solidity docs for advice about optimization and evmVersion // optimizer: {