// File: @openzeppelin\contracts\token\ERC721\IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
// File: node_modules\@openzeppelin\contracts\utils\introspection\IERC165.sol
// OpenZeppelin Contracts v4.4.0 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: @openzeppelin\contracts\token\ERC721\IERC721.sol
// OpenZeppelin Contracts v4.4.0 (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
// File: node_modules\@openzeppelin\contracts\utils\Context.sol
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @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 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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File: @openzeppelin\contracts\access\Ownable.sol
// OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
abstract 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() {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual 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 {
_transferOwnership(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");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// File: @openzeppelin\contracts\utils\math\SafeMath.sol
// OpenZeppelin Contracts v4.4.0 (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// 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 (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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) {
return a + b;
}
/**
* @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 a - b;
}
/**
* @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) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting 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 a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting 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) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* 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) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
// File: contracts\lib\IMintableERC20.sol
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IMintableERC20 {
function mint(address to, uint256 amount) external;
/**
* @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: contracts\consoleStake.sol
pragma solidity ^0.8.0;
contract ConsoleStake is IERC721Receiver, Ownable {
using SafeMath for uint256;
uint256 constant SECINDAY = 86400;
IMintableERC20 public gameToken;
IERC721 public consoleToken;
// struct to store a stake's token, owner, and earning values
struct Stake {
uint256 lastClaim;
address owner;
}
// maps tokenId to stake
mapping(uint256 => Stake) public stakes;
event TokenStaked(address owner, uint256 tokenId);
event TokenUnStaked(address owner, uint256 tokenId);
event GameClaimed(uint256 tokenId, uint256 earned);
constructor(IMintableERC20 _gameToken, IERC721 _consoleToken) {
gameToken = _gameToken;
consoleToken = _consoleToken;
}
function stakeConsoles(uint16[] calldata tokenIds) external {
for (uint i = 0; i < tokenIds.length; i++) {
require(consoleToken.ownerOf(tokenIds[i]) == _msgSender(), "not_token_owner");
consoleToken.transferFrom(_msgSender(), address(this), tokenIds[i]);
_stakeConsole(_msgSender(), tokenIds[i]);
}
}
function unstakeConsoles(uint16[] calldata tokenIds) external {
for (uint i = 0; i < tokenIds.length; i++) {
require(stakes[tokenIds[i]].owner == _msgSender(), "not_token_owner");
consoleToken.transferFrom(address(this), _msgSender(), tokenIds[i]);
_unstakeConsole(_msgSender(), tokenIds[i]);
}
}
function claim(uint16[] calldata tokenIds) external {
uint256 amount = 0;
for (uint i = 0; i < tokenIds.length; i++) {
require(stakes[tokenIds[i]].owner == _msgSender(), "not_token_owner");
amount = amount.add(_claim(tokenIds[i]));
}
// transfer Game token reward
if (amount > 0) {
gameToken.mint(msg.sender, amount * 10 ** 18);
}
}
function onERC721Received(
address,
address from,
uint256,
bytes calldata
) external pure override returns (bytes4) {
require(from == address(0x0), "Cannot send tokens to Barn directly");
return IERC721Receiver.onERC721Received.selector;
}
function _stakeConsole(address account, uint256 tokenId) internal {
stakes[tokenId] = Stake({
lastClaim: block.timestamp,
owner: account
});
emit TokenStaked(account, tokenId);
}
function _unstakeConsole(address account, uint256 tokenId) internal {
delete stakes[tokenId];
emit TokenUnStaked(account, tokenId);
}
/**
update last claim time for stake, returns amount of Game token be rewarded
*/
function _claim(uint16 tokenId) internal returns (uint256){
Stake storage stake = stakes[tokenId];
uint256 amount = block.timestamp.sub(stake.lastClaim).div(SECINDAY);
if (amount > 0) {
stake.lastClaim = stake.lastClaim.add(amount.mul(SECINDAY));
emit GameClaimed(tokenId, amount);
}
return amount;
}
}
// File: contracts\ConsoleStakeV2.sol
pragma solidity ^0.8.0;
contract ConsoleStakeV2 is Ownable {
using SafeMath for uint256;
uint256 constant SECINDAY = 86400;
uint256 constant DECADE_PERIOD = 50; // IN DAYS
uint256 constant INIT_TIME = 1639584000; // GAME TOKEN DEPLOYMENT TIMESTAMP Wed Dec 15 2021 16:00:00 GMT+0000
uint256 constant LPREWARD_SEC = 0.003472 ether; // GAME EMISSION RATE FOR LP STAKING IN A SEC => 300 GAME PER DAY
IMintableERC20 public gameToken;
ConsoleStake public oldStake;
IMintableERC20 public LPToken; // game-eth or game-frax
uint256 public lastLPRewardTime; // Last block timestamp that Games distribution occurs.
uint256 public accGAMEPerShare; // Accumulated Games per share, times 1e12. See below.
// struct to store a stake's token, owner, and earning values
struct Stake {
uint256 lastClaim;
address owner;
}
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt.
}
event DepositLP(address indexed user, uint256 amount);
event WithdrawLP(address indexed user, uint256 amount);
event EmergencyWithdrawLP(address indexed user, uint256 amount);
// maps tokenId to stake
mapping(uint256 => Stake) public stakes;
mapping(address => UserInfo) public userInfo;
constructor(IMintableERC20 _gameToken, IMintableERC20 _lpToken, ConsoleStake _oldStake) {
gameToken = _gameToken;
LPToken = _lpToken;
oldStake = _oldStake;
}
// 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 AMCs on frontend.
function pendingGAME(address _user) external view returns (uint256) {
UserInfo storage user = userInfo[_user];
uint256 lpSupply = LPToken.balanceOf(address(this));
uint256 _accGAMEPerShare = accGAMEPerShare;
if (block.timestamp > lastLPRewardTime && lpSupply != 0) {
uint256 multiplier = getMultiplier(lastLPRewardTime, block.timestamp);
uint256 gameReward = multiplier.mul(emissionRateLPReward());
_accGAMEPerShare = _accGAMEPerShare.add(gameReward.mul(1e12).div(lpSupply));
}
return user.amount.mul(_accGAMEPerShare).div(1e12).sub(user.rewardDebt);
}
// Update reward variables of the given pool to be up-to-date.
function updatePool() public {
if (block.timestamp <= lastLPRewardTime) {
return;
}
uint256 lpSupply = LPToken.balanceOf(address(this));
if (lpSupply == 0) {
lastLPRewardTime = block.timestamp;
return;
}
uint256 multiplier = getMultiplier(lastLPRewardTime, block.timestamp);
uint256 gameReward = multiplier.mul(emissionRateLPReward());
gameToken.mint(address(this), gameReward);
accGAMEPerShare = accGAMEPerShare.add(gameReward.mul(1e12).div(lpSupply));
lastLPRewardTime = block.timestamp;
}
// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw() public {
UserInfo storage user = userInfo[msg.sender];
uint256 amount = user.amount;
user.amount = 0;
user.rewardDebt = 0;
LPToken.transfer(address(msg.sender), amount);
emit EmergencyWithdrawLP(msg.sender, amount);
}
function enterStaking(uint256 _amount) public {
UserInfo storage user = userInfo[msg.sender];
updatePool();
if (user.amount > 0) {
uint256 pending = user.amount.mul(accGAMEPerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
gameToken.transfer(msg.sender, pending);
}
}
if(_amount > 0) {
require(LPToken.transferFrom(address(msg.sender), address(this), _amount));
user.amount = user.amount.add(_amount);
}
user.rewardDebt = user.amount.mul(accGAMEPerShare).div(1e12);
emit DepositLP(msg.sender, _amount);
}
function leaveStaking(uint256 _amount) public {
UserInfo storage user = userInfo[msg.sender];
require(user.amount >= _amount, "withdraw: not good");
updatePool();
uint256 pending = user.amount.mul(accGAMEPerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
gameToken.transfer(msg.sender, pending);
}
if(_amount > 0) {
user.amount = user.amount.sub(_amount);
LPToken.transfer(address(msg.sender), _amount);
}
user.rewardDebt = user.amount.mul(accGAMEPerShare).div(1e12);
emit WithdrawLP(msg.sender, _amount);
}
function emissionRate() public view returns (uint256) {
uint256 periods = block.timestamp.sub(INIT_TIME).div(SECINDAY * DECADE_PERIOD);
return (10 ** 18) / (2 ** periods) ;
}
function emissionRateLPReward() public view returns (uint256) {
uint256 periods = block.timestamp.sub(INIT_TIME).div(SECINDAY * DECADE_PERIOD);
return LPREWARD_SEC / (2 ** periods) ;
}
function calcReward(uint256 _tokenId) public view returns (uint256, uint256) {
uint256 rate = emissionRate();
uint256 lastClaim;
address creator;
(lastClaim, creator) = oldStake.stakes(_tokenId);
if (creator == address(0)) return (0, 0);
Stake storage stake = stakes[_tokenId];
if (stake.lastClaim > lastClaim) {
lastClaim = stake.lastClaim;
}
uint256 slots = block.timestamp.sub(lastClaim).div(SECINDAY);
uint256 amount = slots.mul(rate);
return (amount, slots);
}
function claim(uint16[] calldata tokenIds) external {
uint256 amount = 0;
for (uint i = 0; i < tokenIds.length; i++) {
amount = amount.add(_claim(tokenIds[i]));
}
// transfer Game token reward
if (amount > 0) {
gameToken.mint(msg.sender, amount);
}
}
/**
update last claim time for stake, returns amount of Game token be rewarded
*/
function _claim(uint16 _tokenId) internal returns (uint256){
uint256 lastClaim;
address creator;
(lastClaim, creator) = oldStake.stakes(_tokenId);
require(creator == _msgSender(), "not_stake_owner");
uint256 amount;
uint256 slots;
(amount, slots) = calcReward(_tokenId);
// update stake stroage for next reward
Stake storage stake = stakes[_tokenId];
if (stake.lastClaim > lastClaim) {
stake.lastClaim = stake.lastClaim.add(slots.mul(SECINDAY));
} else {
stake.lastClaim = lastClaim.add(slots.mul(SECINDAY));
}
return amount;
}
}
{
"compilationTarget": {
"ConsoleStakeV2.sol": "ConsoleStakeV2"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IMintableERC20","name":"_gameToken","type":"address"},{"internalType":"contract IMintableERC20","name":"_lpToken","type":"address"},{"internalType":"contract ConsoleStake","name":"_oldStake","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositLP","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdrawLP","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawLP","type":"event"},{"inputs":[],"name":"LPToken","outputs":[{"internalType":"contract IMintableERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accGAMEPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"calcReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"tokenIds","type":"uint16[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emissionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emissionRateLPReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"enterStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gameToken","outputs":[{"internalType":"contract IMintableERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"lastLPRewardTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"leaveStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oldStake","outputs":[{"internalType":"contract ConsoleStake","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"pendingGAME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakes","outputs":[{"internalType":"uint256","name":"lastClaim","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"}]