// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
/*
Copyright 2024 Galxe.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./ITokenUpgrade.sol";
contract GalxeStaking is Pausable, Ownable {
/* ============ Constructor ============ */
constructor(address owner) {
transferOwnership(owner);
}
/* ============ Initializer ============ */
function initialize(IERC20 _token) external onlyOwner {
require(!initialized, "Contract is already initialized");
initialized = true; // Ensure initialize can't be called again
allowedToken = _token;
emit TokenChanged(_token);
}
/* ============ State Variables ============ */
bool private initialized;
uint256 public constant unStakeLimit = 10;
// Timestamp related variables
uint256 public lockTimePeriod;
// token multiplier for future token upgrade
uint256 public multiplier = 60;
// total balance map: userAddress => amount
mapping(address => uint256) public balances;
// UnStakeInfo record user lockInfo, when claimed deleted
struct UnStakeInfo {
uint256 unStakeTime;
uint256 amount;
}
mapping(address => UnStakeInfo[]) public unStakeInfoMap;
// Allowed ERC20 token
IERC20 public allowedToken;
/* ============ Events ============ */
event TokenChanged(IERC20 indexed token);
event TokenUpgraded(IERC20 indexed token, ITokenUpgrade indexed upgrader, uint256 amount, uint256 multiplier);
event TokenAndMultiplierChanged(IERC20 indexed token, uint256 multiplier);
event LockDurationUpdated(uint256 _timePeriodInSeconds);
event TokenStaked(address indexed userAddress, uint256 amount);
event TokenRevokeUnStaked(address indexed userAddress, UnStakeInfo unStakeInfo);
event TokenUnStaked(address indexed userAddress, UnStakeInfo unStakeInfo);
event TokenWithdraw(address indexed userAddress, UnStakeInfo unStakeInfo);
/* ============ External Functions ============ */
function upgradeToken(IERC20 _newToken, ITokenUpgrade upgrade, uint256 _multiplier) external onlyOwner {
require(_multiplier > 0, "Multiplier must be greater than 0");
uint256 amount = allowedToken.balanceOf(address(this)) * multiplier;
require(_newToken.approve(address(upgrade), amount), "Approve token cannot fail");
require(upgrade.upgradeToken(amount), "Upgrade token cannot fail");
allowedToken = _newToken;
multiplier = _multiplier;
emit TokenUpgraded(_newToken, upgrade, amount, _multiplier);
}
// only change token and multiplier, token upgrade should be done somehow manually.
function changeTokenAndMultiplier(IERC20 _token, uint256 _multiplier) external onlyOwner {
require(_multiplier > 0, "Multiplier must be greater than 0");
allowedToken = _token;
multiplier = _multiplier;
emit TokenAndMultiplierChanged(_token, _multiplier);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
/**
* @dev Sets the lock time of unstake in seconds i.e. 3600 = 1 hour
* @param _timePeriodInSeconds amount of seconds to lock time of unstake
*/
function setLockDuration(uint256 _timePeriodInSeconds) public onlyOwner {
lockTimePeriod = _timePeriodInSeconds;
emit LockDurationUpdated(_timePeriodInSeconds);
}
function getUnStakeInfo(address user) external view returns (UnStakeInfo[] memory) {
UnStakeInfo[] memory unStakeInfoArray = unStakeInfoMap[user];
uint256 index = 0;
for (; index < unStakeInfoArray.length; index++) {
unStakeInfoArray[index].amount /= multiplier;
}
return unStakeInfoArray;
}
/**
* @dev User stack token when the contract is not paused.
* @param amount, amount of stake.
*/
function stakeToken(uint256 amount) public whenNotPaused {
require(address(allowedToken) != address(0), "Must be allowed token.");
require(amount <= allowedToken.balanceOf(msg.sender), "Insufficient token balance, please edit amount and try again.");
require(
allowedToken.allowance(msg.sender, address(this)) >= amount,
"Approve tokens first!"
);
_transferToken(msg.sender, address(this), amount);
balances[msg.sender] = balances[msg.sender] + amount * multiplier;
emit TokenStaked(msg.sender, amount);
}
/**
* @dev Allow users to stake tokens allowed by the contract.
* @param amount, amount of unstake.
*/
function unStake(uint256 amount) public whenNotPaused {
amount = amount * multiplier;
require(lockTimePeriod > 0, "Please set the time stamp first, then try again.");
require(address(allowedToken) != address(0), "Must be allowed token.");
require(amount > 0 , "amount is invalid, please edit amount and try again.");
require(unStakeInfoMap[msg.sender].length < unStakeLimit , "The unStake queue has reached the upper limit of 10 and needs to be revoke or withdraw before proceeding.");
require(balances[msg.sender] >= amount, "Insufficient token balance, please edit amount and try again.");
UnStakeInfo memory unStakeInfo = UnStakeInfo(block.timestamp, amount);
// sub balances
balances[msg.sender] = balances[msg.sender] - amount;
unStakeInfoMap[msg.sender].push(unStakeInfo);
unStakeInfo.amount /= multiplier;
emit TokenUnStaked(msg.sender, unStakeInfo);
}
/**
* @dev Allow users renege during the unstack lock cycle.
* @param index, The index of unstakee.
*/
function revokeUnStake(uint256 index) public whenNotPaused {
require(address(allowedToken) != address(0), "Must be allowed token");
require(unStakeInfoMap[msg.sender].length > index, "index is invalid, please refresh the page and try again.");
UnStakeInfo storage unStakeInfo = unStakeInfoMap[msg.sender][index];
balances[msg.sender] = balances[msg.sender] + unStakeInfo.amount;
UnStakeInfo memory tmpInfo = _removeAtIndex(index);
tmpInfo.amount = tmpInfo.amount / multiplier;
emit TokenRevokeUnStaked(msg.sender, tmpInfo);
}
/**
* @dev Allow users to withdraw staked tokens after the correct period of time.
* @param index, The index of unstakee.
*/
function withdrawUnLockedToken(uint256 index) public whenNotPaused {
require(address(allowedToken) != address(0), "Must be allowed token.");
require(unStakeInfoMap[msg.sender].length > index, "index is invalid, please refresh the page and try again.");
if (block.timestamp >= unStakeInfoMap[msg.sender][index].unStakeTime + lockTimePeriod) {
UnStakeInfo memory tmpInfo = _removeAtIndex(index);
tmpInfo.amount = tmpInfo.amount / multiplier;
_transferToken(address(this), msg.sender, tmpInfo.amount);
emit TokenWithdraw(msg.sender, tmpInfo);
} else {
revert("Tokens are only available after correct time period has elapsed");
}
}
/**
* @dev Allow users to withdraw staked tokens after the correct period of time.
*/
function withdrawAllUnLockedToken() public whenNotPaused {
require(address(allowedToken) != address(0), "Must be allowed token.");
require(unStakeInfoMap[msg.sender].length > 0, "unStaking token is 0.");
UnStakeInfo[] storage unStakeInfoArray = unStakeInfoMap[msg.sender];
uint256 amount = 0;
uint256 index = 0;
for (; index < unStakeInfoArray.length; index++) {
if (block.timestamp >= unStakeInfoArray[index].unStakeTime + lockTimePeriod) {
amount = amount + unStakeInfoArray[index].amount;
} else {
break;
}
}
if (amount > 0) {
_removeBeforeIndex(index);
_transferToken(address(this), msg.sender, amount / multiplier);
} else {
revert("Tokens are only available after correct time period has elapsed");
}
}
/**
* * @dev Get stake record by user address.
*/
function getStakeAmount(address _user) external view returns (uint256) {
return balances[_user] / multiplier;
}
/**
* * @dev Pay fees for a given plan.
*/
function _transferToken(address from, address to, uint256 amount) private {
if (from == address(this)) {
require(allowedToken.transfer(to, amount), "Transfer token failed");
} else {
require(allowedToken.transferFrom(from, to, amount), "Transfer token failed");
}
}
/**
* @dev Recovers a `tokenAmount` of the ERC20 `tokenAddress` locked into this contract
* and sends them to the `tokenReceiver` address.
*
* @param tokenAddress The contract address of the token to recover.
* @param tokenReceiver The address that will receive the recovered tokens.
* @param tokenAmount Number of tokens to be recovered.
*/
function recoverERC20(IERC20 tokenAddress, address tokenReceiver, uint256 tokenAmount) external onlyOwner {
require(tokenAddress != allowedToken, "only not allowedToken can be recover");
require(tokenAddress.transfer(tokenReceiver, tokenAmount), "Transfer token failed");
}
function _removeAtIndex(uint256 index) private returns (UnStakeInfo memory) {
require(index < unStakeInfoMap[msg.sender].length, "Index out of bounds");
UnStakeInfo[] storage unStakeInfoArray = unStakeInfoMap[msg.sender];
UnStakeInfo memory tmpInfo = unStakeInfoArray[index];
for (uint256 i = index; i < unStakeInfoArray.length - 1; i++) {
unStakeInfoArray[i] = unStakeInfoArray[i + 1];
}
unStakeInfoArray.pop();
return tmpInfo;
}
function _removeBeforeIndex(uint256 index) private {
require(index <= unStakeInfoMap[msg.sender].length, "Index out of bounds");
UnStakeInfo[] storage unStakeInfoArray = unStakeInfoMap[msg.sender];
uint256 length = unStakeInfoArray.length;
for (uint256 i = 0; i < index; i++) {
unStakeInfoArray[i].amount = unStakeInfoArray[i].amount / multiplier;
emit TokenWithdraw(msg.sender, unStakeInfoArray[i]);
}
for (uint256 i = 0; i < length; i++) {
if (i < length - index) {
unStakeInfoArray[i] = unStakeInfoArray[i + index];
} else {
unStakeInfoArray.pop();
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
/*
Copyright 2024 Galxe.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity ^0.8.0;
interface ITokenUpgrade {
// This function upgrade the erc20 token of the previous version to the new version.
// Caller must pre-approve the amount of tokens to be upgraded to this contract,
// and specify the amount in the amount parameter.
function upgradeToken(uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/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.
*/
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 Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
{
"compilationTarget": {
"contracts/GalxeStaking/GalxeStaking.sol": "GalxeStaking"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "none",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_timePeriodInSeconds","type":"uint256"}],"name":"LockDurationUpdated","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"TokenAndMultiplierChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"}],"name":"TokenChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"components":[{"internalType":"uint256","name":"unStakeTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct GalxeStaking.UnStakeInfo","name":"unStakeInfo","type":"tuple"}],"name":"TokenRevokeUnStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"components":[{"internalType":"uint256","name":"unStakeTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct GalxeStaking.UnStakeInfo","name":"unStakeInfo","type":"tuple"}],"name":"TokenUnStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"contract ITokenUpgrade","name":"upgrader","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"TokenUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"components":[{"internalType":"uint256","name":"unStakeTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct GalxeStaking.UnStakeInfo","name":"unStakeInfo","type":"tuple"}],"name":"TokenWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"allowedToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint256","name":"_multiplier","type":"uint256"}],"name":"changeTokenAndMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getStakeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUnStakeInfo","outputs":[{"components":[{"internalType":"uint256","name":"unStakeTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct GalxeStaking.UnStakeInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockTimePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"address","name":"tokenReceiver","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"revokeUnStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timePeriodInSeconds","type":"uint256"}],"name":"setLockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stakeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"unStakeInfoMap","outputs":[{"internalType":"uint256","name":"unStakeTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unStakeLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_newToken","type":"address"},{"internalType":"contract ITokenUpgrade","name":"upgrade","type":"address"},{"internalType":"uint256","name":"_multiplier","type":"uint256"}],"name":"upgradeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAllUnLockedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"withdrawUnLockedToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]