账户
0x78...4a6a
0x78...4A6A

0x78...4A6A

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.19+commit.7dd6d404
语言
Solidity
合同源代码
文件 1 的 1:Staking.sol
//SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);

    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address to, uint256 value) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);
}


abstract contract ReentrancyGuard {
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = _NOT_ENTERED;
    }

    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        if (_status == _ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        _status = _NOT_ENTERED;
    }

    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

abstract contract Ownable is Context {
    address private _owner;

    error OwnableUnauthorizedAccount(address account);

    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor(address initialOwner) {
        _transferOwnership(initialOwner);
    }

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    function owner() public view virtual returns (address) {
        return _owner;
    }

    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}


contract Staking is Ownable, ReentrancyGuard {

    uint256 private constant DENOMINATOR = 10000;
    uint256 private constant REWARD_PERIOD = 365 days;
    uint256 private constant EARLY_WITHDRAWAL_PENALTY = 60;

    bool private lockupPeriodEnabled = true;

    uint256 private withdrawalTaxes = 0;

    struct Pool {
        uint256 apy;
        uint256 lockupPeriod;
        bool isOpen;
        uint256 totalStaked;
        uint256 totalWithdrawn;
    }

    Pool firstPool = Pool(3500, 14 days, false, 0, 0);
    Pool secondPool = Pool(1, 30 days, false, 0, 0);
    Pool thirdPool = Pool(1, 60 days, false, 0, 0);

    struct User {
        uint256 stakedOnTimestamp;
        uint256 stakeEndTimestamp;
        uint256 stakedAmount;
        uint256 lockupTimestamp;
        uint256 lastActionTimestamp;
    }

    IERC20 private stakeToken = IERC20(0x1E1D8d6d00E0b8f9102d2b3D77de439774A70E97);

    mapping(uint256 => Pool) private pools; // Pool ID => Pool struct

    mapping(address => mapping(uint256 => User)) private userInPool; // User address => Pool ID => User struct of this individual pool

    constructor() Ownable(msg.sender) {
        pools[0] = firstPool;
        pools[1] = secondPool;
        pools[2] = thirdPool;
    }

    function stake(uint256 poolId, uint256 amount) external nonReentrant {
        require(amount > 0, "Amount must be greater than 0");
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2");
        require(pools[poolId].isOpen, "Pool is not open");
        
        IERC20(stakeToken).transferFrom(msg.sender, address(this), amount);

        Pool memory pool = pools[poolId];

        User memory user = userInPool[msg.sender][poolId];

        if (user.stakedAmount > 0) {

            uint256 secondsPassed = block.timestamp - user.lastActionTimestamp;

            uint256 rewards = calculateRewards(msg.sender, poolId, secondsPassed, user.stakedAmount);

            uint256 addAmount = amount + rewards;

            userInPool[msg.sender][poolId].lastActionTimestamp = block.timestamp;

            userInPool[msg.sender][poolId].stakedAmount += addAmount;

            userInPool[msg.sender][poolId].lockupTimestamp = block.timestamp + pool.lockupPeriod;

            userInPool[msg.sender][poolId].stakeEndTimestamp = block.timestamp + pool.lockupPeriod;

            pools[poolId].totalStaked += addAmount;

        } else {
            
            userInPool[msg.sender][poolId] = User(block.timestamp, block.timestamp + pool.lockupPeriod, amount, block.timestamp + pool.lockupPeriod, block.timestamp);

            pools[poolId].totalStaked += amount;

        }

        emit newStake(msg.sender, poolId, amount);
    }

    function reStake(uint256 poolId) external nonReentrant {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2");
        require(pools[poolId].isOpen, "Pool is not open");

        Pool memory pool = pools[poolId];

        User memory user = userInPool[msg.sender][poolId];

        require(user.stakedAmount > 0, "User has no staked amount in this pool.");

        uint256 secondsPassed = block.timestamp - user.lastActionTimestamp;

        uint256 rewards = calculateRewards(msg.sender, poolId, secondsPassed, user.stakedAmount);

        userInPool[msg.sender][poolId].lastActionTimestamp = block.timestamp;

        userInPool[msg.sender][poolId].stakedAmount += rewards;

        userInPool[msg.sender][poolId].stakeEndTimestamp = block.timestamp + pool.lockupPeriod;

        userInPool[msg.sender][poolId].lockupTimestamp = block.timestamp + pool.lockupPeriod;

        pools[poolId].totalStaked += rewards;

        emit reStaked(msg.sender, poolId, rewards);
    }

    function claimRewards(uint256 poolId) external nonReentrant {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");

        User memory user = userInPool[msg.sender][poolId];

        require(user.stakedAmount > 0, "User has no staked amount in this pool.");

        uint256 secondsPassed = block.timestamp - user.lastActionTimestamp;

        uint256 rewards = calculateRewards(msg.sender, poolId, secondsPassed, user.stakedAmount);

        require(rewards > 0, "No rewards to claim.");

        userInPool[msg.sender][poolId].lastActionTimestamp = block.timestamp;

        IERC20(stakeToken).transfer(msg.sender, rewards);

        emit claimedRewards(msg.sender, poolId, rewards);
    }

    function withdrawStake(uint256 poolId) external nonReentrant {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");

        User memory user = userInPool[msg.sender][poolId];

        require(user.stakedAmount > 0, "User has no staked amount in this pool.");
        
        if(lockupPeriodEnabled) {
            require(block.timestamp >= user.lockupTimestamp, "Lockup period has not passed yet.");
        }

        uint256 secondsPassed = block.timestamp - user.lastActionTimestamp;

        uint256 rewards = calculateRewards(msg.sender, poolId, secondsPassed, user.stakedAmount);

        uint256 totalAmount;

        if(rewards > 0) {
            totalAmount = user.stakedAmount + rewards;
        } else {
            totalAmount = user.stakedAmount;
        }

        userInPool[msg.sender][poolId].stakedAmount = 0;
        userInPool[msg.sender][poolId].stakedOnTimestamp = 0;
        userInPool[msg.sender][poolId].stakeEndTimestamp = 0;
        userInPool[msg.sender][poolId].lockupTimestamp = 0;
        userInPool[msg.sender][poolId].lastActionTimestamp = 0;

        pools[poolId].totalStaked -= (totalAmount - rewards);
        pools[poolId].totalWithdrawn += totalAmount;

        IERC20(stakeToken).transfer(msg.sender, totalAmount);

        emit withdrewStake(msg.sender, poolId, totalAmount);
    }

    function forceWithdrawStake(uint256 poolId) external nonReentrant {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");

        User memory user = userInPool[msg.sender][poolId];

        require(user.stakedAmount > 0, "User has no staked amount in this pool.");

        uint256 secondsPassed = block.timestamp - user.lastActionTimestamp;

        uint256 rewards = calculateRewards(msg.sender, poolId, secondsPassed, user.stakedAmount);

        uint256 totalAmount;

        if(rewards > 0) {
            totalAmount = user.stakedAmount + rewards;
        } else {
            totalAmount = user.stakedAmount;
        }

        uint256 earlyWithdrawalPenalty = totalAmount * EARLY_WITHDRAWAL_PENALTY / 100;

        totalAmount -= earlyWithdrawalPenalty;

        withdrawalTaxes += earlyWithdrawalPenalty;

        userInPool[msg.sender][poolId].stakedAmount = 0;
        userInPool[msg.sender][poolId].stakedOnTimestamp = 0;
        userInPool[msg.sender][poolId].stakeEndTimestamp = 0;
        userInPool[msg.sender][poolId].lockupTimestamp = 0;
        userInPool[msg.sender][poolId].lastActionTimestamp = 0;

        pools[poolId].totalStaked -= (totalAmount - rewards) + earlyWithdrawalPenalty;
        pools[poolId].totalWithdrawn += totalAmount + earlyWithdrawalPenalty;

        IERC20(stakeToken).transfer(msg.sender, totalAmount);

        emit forcedWithdrawStake(msg.sender, poolId, totalAmount);
    }

    function getTotalStakedPool(uint256 poolId) external view returns (uint256) {
        return pools[poolId].totalStaked;
    }

    function getTotalWithdrawnPool(uint256 poolId) external view returns (uint256) {
        return pools[poolId].totalWithdrawn;
    }

    function getPool(uint256 poolId) external view returns (Pool memory) {
        return pools[poolId];
    }

    function getUserInPool(address user, uint256 poolId) external view returns (User memory) {
        return userInPool[user][poolId];
    }

    function getPoolApy(uint256 poolId) external view returns (uint256) {
        return pools[poolId].apy;
    }

    function getPoolLockupPeriod(uint256 poolId) external view returns (uint256) {
        return pools[poolId].lockupPeriod;
    }

    function getPoolIsOpen(uint256 poolId) external view returns (bool) {
        return pools[poolId].isOpen;
    }

    function getLockupPeriodEnabled() external view returns (bool) {
        return lockupPeriodEnabled;
    }

    function getUserStakedAmount(address user, uint256 poolId) external view returns (uint256) {
        return userInPool[user][poolId].stakedAmount;
    }

    function getUserLockupEndTimestamp(address user, uint256 poolId) external view returns (uint256) {
        return userInPool[user][poolId].lockupTimestamp;
    }

    function getUserStakedOnTimestamp(address user, uint256 poolId) external view returns (uint256) {
        return userInPool[user][poolId].stakedOnTimestamp;
    }

    function getUserStakeEndTimestamp(address user, uint256 poolId) external view returns (uint256) {
        return userInPool[user][poolId].stakeEndTimestamp;
    }

    function getUserPendingRewards(address user, uint256 poolId) external view returns (uint256) {
        User memory _user = userInPool[user][poolId];

        uint256 secondsPassed = block.timestamp - _user.lastActionTimestamp;

        uint256 rewards = calculateRewards(user, poolId, secondsPassed, _user.stakedAmount);

        return rewards;
    }

    function openAllPools() external onlyOwner {
        pools[0].isOpen = true;
        pools[1].isOpen = true;
        pools[2].isOpen = true;
        emit openedAllPools();
    }

    function closeAllPools() external onlyOwner {
        pools[0].isOpen = false;
        pools[1].isOpen = false;
        pools[2].isOpen = false;
        emit closedAllPools();
    }

    function setApy(uint256 poolId, uint256 _apy) external onlyOwner {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");
        require(_apy >= 0, "APY must be greater than or equal to 0.");
        require(_apy <= 10000, "APY must be less than or equal to 100.");
        pools[poolId].apy = _apy;
        emit changedApy(poolId, _apy);
    }

    function setPoolOpen(uint256 poolId, bool _isOpen) external onlyOwner {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");
        pools[poolId].isOpen = _isOpen;
        emit switchedPoolOpen(poolId, _isOpen);
    }
        
    function setLockupPeriodEnabled(bool _lockupPeriodEnabled) external onlyOwner {
        lockupPeriodEnabled = _lockupPeriodEnabled;
        emit switchedLockupPeriodEnabled(_lockupPeriodEnabled);
    }

    function ownerWithdraw(uint256 amount) external onlyOwner {
        uint256 balance = IERC20(stakeToken).balanceOf(address(this));
        uint256 withdrawable = balance - pools[0].totalStaked - pools[1].totalStaked - pools[2].totalStaked;
        require(amount <= withdrawable, "Amount must be less than or equal to withdrawable amount.");
        IERC20(stakeToken).transfer(msg.sender, amount);
        emit ownerWithdrawed(amount);
    }

    function calculateRewards(address userAddr, uint256 poolId, uint256 secondsPassed, uint256 amount) private view returns (uint256) {
        require(poolId >= 0 && poolId <= 2, "Pool ID must be between 0 and 2.");

        Pool memory pool = pools[poolId];
        User memory user = userInPool[userAddr][poolId];

        if(block.timestamp >= user.stakeEndTimestamp && user.lastActionTimestamp < user.stakeEndTimestamp) {
            secondsPassed = user.stakeEndTimestamp - user.lastActionTimestamp;
        }

        uint256 apy = pool.apy;

        uint256 rewards = (secondsPassed * apy * amount) / (REWARD_PERIOD * DENOMINATOR);

        return rewards;
    }

    event switchedLockupPeriodEnabled(bool _lockupPeriodEnabled);
    event switchedPoolOpen(uint256 poolId, bool _isOpen);
    event changedApy(uint256 poolId, uint256 _apy);
    event ownerWithdrawed(uint256 amount);
    event openedAllPools();
    event closedAllPools();
    event newStake(address user, uint256 poolId, uint256 amount);
    event reStaked(address user, uint256 poolId, uint256 amount);
    event claimedRewards(address user, uint256 poolId, uint256 amount);
    event withdrewStake(address user, uint256 poolId, uint256 amount);
    event forcedWithdrawStake(address user, uint256 poolId, uint256 amount);

}
设置
{
  "compilationTarget": {
    "Staking.sol": "Staking"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"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":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_apy","type":"uint256"}],"name":"changedApy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimedRewards","type":"event"},{"anonymous":false,"inputs":[],"name":"closedAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forcedWithdrawStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"newStake","type":"event"},{"anonymous":false,"inputs":[],"name":"openedAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ownerWithdrawed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_lockupPeriodEnabled","type":"bool"}],"name":"switchedLockupPeriodEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_isOpen","type":"bool"}],"name":"switchedPoolOpen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrewStake","type":"event"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"forceWithdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLockupPeriodEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPool","outputs":[{"components":[{"internalType":"uint256","name":"apy","type":"uint256"},{"internalType":"uint256","name":"lockupPeriod","type":"uint256"},{"internalType":"bool","name":"isOpen","type":"bool"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint256","name":"totalWithdrawn","type":"uint256"}],"internalType":"struct Staking.Pool","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPoolApy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPoolIsOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getPoolLockupPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getTotalStakedPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getTotalWithdrawnPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserInPool","outputs":[{"components":[{"internalType":"uint256","name":"stakedOnTimestamp","type":"uint256"},{"internalType":"uint256","name":"stakeEndTimestamp","type":"uint256"},{"internalType":"uint256","name":"stakedAmount","type":"uint256"},{"internalType":"uint256","name":"lockupTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastActionTimestamp","type":"uint256"}],"internalType":"struct Staking.User","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserLockupEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserPendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserStakeEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserStakedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getUserStakedOnTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ownerWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"reStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256","name":"_apy","type":"uint256"}],"name":"setApy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_lockupPeriodEnabled","type":"bool"}],"name":"setLockupPeriodEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"bool","name":"_isOpen","type":"bool"}],"name":"setPoolOpen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"}]