From 8dd15090e483826264d7608f612a3ef99930836c Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 12:36:19 +0800 Subject: [PATCH] wip --- omnichain/staking/contracts/Staking.sol | 33 +- omnichain/staking/flat | 924 ------------------------ 2 files changed, 20 insertions(+), 937 deletions(-) delete mode 100644 omnichain/staking/flat diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 30d56f28..5e230d0f 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -10,6 +10,7 @@ contract Staking is ERC20, zContract { error SenderNotSystemContract(); error WrongChain(); error NotAuthorizedToClaim(); + error UnknownAction(); SystemContract public immutable systemContract; uint256 public immutable chainID; @@ -52,14 +53,22 @@ contract Staking is ERC20, zContract { address staker = BytesHelperLib.bytesToAddress(context.origin, 0); address beneficiary; + uint32 action; if (context.chainID == 18332) { beneficiary = BytesHelperLib.bytesToAddress(message, 0); + action = BytesHelperLib.bytesToUint32(message, 20); } else { - beneficiary = abi.decode(message, (address)); + (beneficiary, action) = abi.decode(message, (address, uint32)); } - stakeZRC(staker, beneficiary, amount); + if (action == 1) { + stakeZRC(staker, beneficiary, amount); + } else if (action == 2) { + unstakeZRC(staker, amount); + } else { + revert UnknownAction(); + } } function stakeZRC( @@ -103,10 +112,10 @@ contract Staking is ERC20, zContract { emit RewardsClaimed(staker, rewardAmount); } - function unstakeZRC(uint256 amount) external { - require(stakes[msg.sender] >= amount, "Insufficient staked balance"); + function unstakeZRC(address staker, uint256 amount) internal { + require(stakes[staker] >= amount, "Insufficient staked balance"); - updateRewards(msg.sender); + updateRewards(staker); address zrc20 = systemContract.gasCoinZRC20ByChainId(chainID); (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); @@ -117,20 +126,18 @@ contract Staking is ERC20, zContract { bytes memory recipient; if (chainID == 18332) { - recipient = abi.encodePacked( - BytesHelperLib.addressToBytes(msg.sender) - ); + recipient = abi.encodePacked(BytesHelperLib.addressToBytes(staker)); } else { - recipient = abi.encodePacked(msg.sender); + recipient = abi.encodePacked(staker); } IZRC20(zrc20).withdraw(recipient, amount - gasFee); - stakes[msg.sender] -= amount; - require(stakes[msg.sender] <= amount, "Underflow detected"); + stakes[staker] -= amount; + require(stakes[staker] <= amount, "Underflow detected"); - lastStakeTime[msg.sender] = block.timestamp; + lastStakeTime[staker] = block.timestamp; - emit Unstaked(msg.sender, amount); + emit Unstaked(staker, amount); } function queryRewards(address account) public view returns (uint256) { diff --git a/omnichain/staking/flat b/omnichain/staking/flat deleted file mode 100644 index 5ec2bf9e..00000000 --- a/omnichain/staking/flat +++ /dev/null @@ -1,924 +0,0 @@ -// Sources flattened with hardhat v2.17.2 https://hardhat.org - - - -// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.9.3 - - -// 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); -} - - -// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v4.9.3 - - -// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface for the optional metadata functions from the ERC20 standard. - * - * _Available since v4.1._ - */ -interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} - - -// File @openzeppelin/contracts/utils/Context.sol@v4.9.3 - - -// OpenZeppelin Contracts v4.4.1 (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/token/ERC20/ERC20.sol@v4.9.3 - - -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * The default value of {decimals} is 18. To change this, you should override - * this function so it returns a different value. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead 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, IERC20Metadata { - mapping(address => uint256) private _balances; - - mapping(address => mapping(address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - /** - * @dev Sets the values for {name} and {symbol}. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual override 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 default value returned by this function, unless - * it's overridden. - * - * 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 virtual override returns (uint8) { - return 18; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address to, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _transfer(owner, to, 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}. - * - * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _approve(owner, 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}. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - * - the caller must have allowance for ``from``'s tokens of at least - * `amount`. - */ - function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { - address spender = _msgSender(); - _spendAllowance(from, spender, amount); - _transfer(from, to, 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 {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) { - address owner = _msgSender(); - _approve(owner, spender, allowance(owner, spender) + 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) { - address owner = _msgSender(); - uint256 currentAllowance = allowance(owner, spender); - require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); - unchecked { - _approve(owner, spender, currentAllowance - subtractedValue); - } - - return true; - } - - /** - * @dev Moves `amount` of tokens from `from` to `to`. - * - * This 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: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - */ - function _transfer(address from, address to, uint256 amount) internal virtual { - require(from != address(0), "ERC20: transfer from the zero address"); - require(to != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(from, to, amount); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by - // decrementing then incrementing. - _balances[to] += amount; - } - - emit Transfer(from, to, amount); - - _afterTokenTransfer(from, to, 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: - * - * - `account` 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 += amount; - unchecked { - // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. - _balances[account] += amount; - } - emit Transfer(address(0), account, amount); - - _afterTokenTransfer(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); - - uint256 accountBalance = _balances[account]; - require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); - unchecked { - _balances[account] = accountBalance - amount; - // Overflow not possible: amount <= accountBalance <= totalSupply. - _totalSupply -= amount; - } - - emit Transfer(account, address(0), amount); - - _afterTokenTransfer(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 Updates `owner` s allowance for `spender` based on spent `amount`. - * - * Does not update the allowance amount in case of infinite allowance. - * Revert if not enough allowance is available. - * - * Might emit an {Approval} event. - */ - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC20: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } - - /** - * @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 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 {} - - /** - * @dev Hook that is called after any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * has been transferred to `to`. - * - when `from` is zero, `amount` tokens have been minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} -} - - -// File @zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol@v2.1.0 - - -pragma solidity 0.8.7; - -interface IZRC20 { - function totalSupply() external view returns (uint256); - - function balanceOf(address account) external view returns (uint256); - - function transfer(address recipient, uint256 amount) external returns (bool); - - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 amount) external returns (bool); - - function decreaseAllowance(address spender, uint256 amount) external returns (bool); - - function increaseAllowance(address spender, uint256 amount) external returns (bool); - - function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); - - function deposit(address to, uint256 amount) external returns (bool); - - function burn(address account, uint256 amount) external returns (bool); - - function withdraw(bytes memory to, uint256 amount) external returns (bool); - - function withdrawGasFee() external view returns (address, uint256); - - function PROTOCOL_FEE() external view returns (uint256); - - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); - event Deposit(bytes from, address indexed to, uint256 value); - event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasFee, uint256 protocolFlatFee); - event UpdatedSystemContract(address systemContract); - event UpdatedGasLimit(uint256 gasLimit); - event UpdatedProtocolFlatFee(uint256 protocolFlatFee); -} - - -// File @zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol@v2.1.0 - - -pragma solidity 0.8.7; - -struct zContext { - bytes origin; - address sender; - uint256 chainID; -} - -interface zContract { - function onCrossChainCall( - zContext calldata context, - address zrc20, - uint256 amount, - bytes calldata message - ) external; -} - - -// File @zetachain/protocol-contracts/contracts/zevm/SystemContract.sol@v2.1.0 - - -pragma solidity 0.8.7; - - -/** - * @dev Custom errors for SystemContract - */ -interface SystemContractErrors { - error CallerIsNotFungibleModule(); - error InvalidTarget(); - error CantBeIdenticalAddresses(); - error CantBeZeroAddress(); - error ZeroAddress(); -} - -/** - * @dev The system contract it's called by the protocol to interact with the blockchain. - * Also includes a lot of tools to make easier to interact with ZetaChain. - */ -contract SystemContract is SystemContractErrors { - /// @notice Map to know the gas price of each chain given a chain id. - mapping(uint256 => uint256) public gasPriceByChainId; - /// @notice Map to know the ZRC20 address of a token given a chain id, ex zETH, zBNB etc. - mapping(uint256 => address) public gasCoinZRC20ByChainId; - // @dev: Map to know uniswap V2 pool of ZETA/ZRC20 given a chain id. This refer to the build in uniswap deployed at genesis. - mapping(uint256 => address) public gasZetaPoolByChainId; - - /// @notice Fungible address is always the same, it's on protocol level. - address public constant FUNGIBLE_MODULE_ADDRESS = 0x735b14BB79463307AAcBED86DAf3322B1e6226aB; - /// @notice Uniswap V2 addresses. - address public immutable uniswapv2FactoryAddress; - address public immutable uniswapv2Router02Address; - /// @notice Address of the wrapped ZETA to interact with Uniswap V2. - address public wZetaContractAddress; - /// @notice Address of ZEVM Zeta Connector. - address public zetaConnectorZEVMAddress; - - /// @notice Custom SystemContract errors. - event SystemContractDeployed(); - event SetGasPrice(uint256, uint256); - event SetGasCoin(uint256, address); - event SetGasZetaPool(uint256, address); - event SetWZeta(address); - event SetConnectorZEVM(address); - - /** - * @dev Only fungible module can deploy a system contract. - */ - constructor(address wzeta_, address uniswapv2Factory_, address uniswapv2Router02_) { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - wZetaContractAddress = wzeta_; - uniswapv2FactoryAddress = uniswapv2Factory_; - uniswapv2Router02Address = uniswapv2Router02_; - emit SystemContractDeployed(); - } - - /** - * @dev Deposit foreign coins into ZRC20 and call user specified contract on zEVM. - * @param context, context data for deposit. - * @param zrc20, zrc20 address for deposit. - * @param amount, amount to deposit. - * @param target, contract address to make a call after deposit. - * @param message, calldata for a call. - */ - function depositAndCall( - zContext calldata context, - address zrc20, - uint256 amount, - address target, - bytes calldata message - ) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - if (target == FUNGIBLE_MODULE_ADDRESS || target == address(this)) revert InvalidTarget(); - - IZRC20(zrc20).deposit(target, amount); - zContract(target).onCrossChainCall(context, zrc20, amount, message); - } - - /** - * @dev Sort token addresses lexicographically. Used to handle return values from pairs sorted in the order. - * @param tokenA, tokenA address. - * @param tokenB, tokenB address. - * @return token0 token1, returns sorted token addresses,. - */ - function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { - if (tokenA == tokenB) revert CantBeIdenticalAddresses(); - (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - if (token0 == address(0)) revert CantBeZeroAddress(); - } - - /** - * @dev Calculates the CREATE2 address for a pair without making any external calls. - * @param factory, factory address. - * @param tokenA, tokenA address. - * @param tokenB, tokenB address. - * @return pair tokens pair address. - */ - function uniswapv2PairFor(address factory, address tokenA, address tokenB) public pure returns (address pair) { - (address token0, address token1) = sortTokens(tokenA, tokenB); - pair = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - hex"ff", - factory, - keccak256(abi.encodePacked(token0, token1)), - hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash - ) - ) - ) - ) - ); - } - - /** - * @dev Fungible module updates the gas price oracle periodically. - * @param chainID, chain id. - * @param price, new gas price. - */ - function setGasPrice(uint256 chainID, uint256 price) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - gasPriceByChainId[chainID] = price; - emit SetGasPrice(chainID, price); - } - - /** - * @dev Setter for gasCoinZRC20ByChainId map. - * @param chainID, chain id. - * @param zrc20, ZRC20 address. - */ - function setGasCoinZRC20(uint256 chainID, address zrc20) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - gasCoinZRC20ByChainId[chainID] = zrc20; - emit SetGasCoin(chainID, zrc20); - } - - /** - * @dev Set the pool wzeta/erc20 address. - * @param chainID, chain id. - * @param erc20, pair for uniswap wzeta/erc20. - */ - function setGasZetaPool(uint256 chainID, address erc20) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - address pool = uniswapv2PairFor(uniswapv2FactoryAddress, wZetaContractAddress, erc20); - gasZetaPoolByChainId[chainID] = pool; - emit SetGasZetaPool(chainID, pool); - } - - /** - * @dev Setter for wrapped ZETA address. - * @param addr, wzeta new address. - */ - function setWZETAContractAddress(address addr) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - if (addr == address(0)) revert ZeroAddress(); - wZetaContractAddress = addr; - emit SetWZeta(wZetaContractAddress); - } - - /** - * @dev Setter for zetaConnector ZEVM Address - * @param addr, zeta connector new address. - */ - function setConnectorZEVMAddress(address addr) external { - if (msg.sender != FUNGIBLE_MODULE_ADDRESS) revert CallerIsNotFungibleModule(); - if (addr == address(0)) revert ZeroAddress(); - zetaConnectorZEVMAddress = addr; - emit SetConnectorZEVM(zetaConnectorZEVMAddress); - } -} - - -// File @zetachain/toolkit/contracts/BytesHelperLib.sol@v2.1.2 - - -pragma solidity =0.8.7; - -library BytesHelperLib { - function bytesToAddress( - bytes calldata data, - uint256 offset - ) internal pure returns (address output) { - bytes memory b = data[offset:offset + 20]; - assembly { - output := mload(add(b, 20)) - } - } - - function bytesToUint32( - bytes calldata data, - uint256 offset - ) internal pure returns (uint32 output) { - bytes memory b = data[offset:offset + 4]; - assembly { - output := mload(add(b, 4)) - } - } - - function addressToBytes( - address someAddress - ) internal pure returns (bytes32) { - return bytes32(uint256(uint160(someAddress))); - } -} - - -// File contracts/Staking.sol - - -pragma solidity 0.8.7; - - - - -contract Staking is ERC20, zContract { - error SenderNotSystemContract(); - error WrongChain(); - error NotAuthorizedToClaim(); - - SystemContract public immutable systemContract; - uint256 public immutable chainID; - - mapping(address => uint256) public stakes; - mapping(address => address) public beneficiaries; - mapping(address => uint256) public lastStakeTime; - uint256 public rewardRate = 1; - - event Staked( - address indexed staker, - address indexed beneficiary, - uint256 amount - ); - event RewardsClaimed(address indexed staker, uint256 rewardAmount); - event Unstaked(address indexed staker, uint256 amount); - - constructor( - string memory name_, - string memory symbol_, - uint256 chainID_, - address systemContractAddress - ) ERC20(name_, symbol_) { - systemContract = SystemContract(systemContractAddress); - chainID = chainID_; - } - - function onCrossChainCall( - zContext calldata context, - address zrc20, - uint256 amount, - bytes calldata message - ) external override { - if (msg.sender != address(systemContract)) { - revert SenderNotSystemContract(); - } - - address acceptedZRC20 = systemContract.gasCoinZRC20ByChainId(chainID); - if (zrc20 != acceptedZRC20) revert WrongChain(); - - address staker = BytesHelperLib.bytesToAddress(context.origin, 0); - address beneficiary; - - if (context.chainID == 18332) { - beneficiary = BytesHelperLib.bytesToAddress(message, 0); - } else { - beneficiary = abi.decode(message, (address)); - } - - stakeZRC(staker, beneficiary, amount); - } - - function stakeZRC( - address staker, - address beneficiary, - uint256 amount - ) internal { - stakes[staker] += amount; - require(stakes[staker] >= amount, "Overflow detected"); // Check for overflows - - if (beneficiaries[staker] == address(0)) { - beneficiaries[staker] = beneficiary; - } - - lastStakeTime[staker] = block.timestamp; - updateRewards(staker); - - emit Staked(staker, beneficiary, amount); // Emitting Staked event - } - - function updateRewards(address staker) internal { - uint256 timeDifference = block.timestamp - lastStakeTime[staker]; - uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; - require(rewardAmount >= timeDifference, "Overflow detected"); // Check for overflows - - _mint(beneficiaries[staker], rewardAmount); - lastStakeTime[staker] = block.timestamp; - } - - function claimRewards(address staker) external { - require( - beneficiaries[staker] == msg.sender, - "Not authorized to claim rewards" - ); - - uint256 rewardAmount = queryRewards(staker); - require(rewardAmount > 0, "No rewards to claim"); - - updateRewards(staker); - - emit RewardsClaimed(staker, rewardAmount); // Emitting RewardsClaimed event - } - - function unstakeZRC(uint256 amount) external { - require(stakes[msg.sender] >= amount, "Insufficient staked balance"); - - updateRewards(msg.sender); - - address zrc20 = systemContract.gasCoinZRC20ByChainId(chainID); - (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); - - require(amount >= gasFee, "Amount should be greater than the gas fee"); - - IZRC20(zrc20).approve(zrc20, gasFee); - - bytes memory recipient; - if (chainID == 18332) { - recipient = abi.encodePacked( - BytesHelperLib.addressToBytes(msg.sender) - ); - } else { - recipient = abi.encodePacked(msg.sender); - } - - IZRC20(zrc20).withdraw(recipient, amount - gasFee); - stakes[msg.sender] -= amount; - require(stakes[msg.sender] <= amount, "Underflow detected"); // Check for underflows - - lastStakeTime[msg.sender] = block.timestamp; - - emit Unstaked(msg.sender, amount); // Emitting Unstaked event - } - - function queryRewards(address account) public view returns (uint256) { - uint256 timeDifference = block.timestamp - lastStakeTime[account]; - uint256 rewardAmount = timeDifference * stakes[account] * rewardRate; - return rewardAmount; - } -}