From 32e19707294f193044f10750f6f81af14cebb8c8 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 4 Sep 2023 12:58:50 +0900 Subject: [PATCH 01/27] staking bitcoin --- omnichain/staking/contracts/Staking.sol | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 02804806..ca224c34 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -43,7 +43,13 @@ contract Staking is ERC20, zContract { if (zrc20 != acceptedZRC20) revert WrongChain(); address staker = BytesHelperLib.bytesToAddress(context.origin, 0); - address beneficiary = abi.decode(message, (address)); + address beneficiary; + + if (context.chainID == 18832) { + beneficiary = BytesHelperLib.bytesToAddress(message, 0); + } else { + beneficiary = abi.decode(message, (address)); + } stakeZRC(staker, beneficiary, amount); } @@ -88,7 +94,18 @@ contract Staking is ERC20, zContract { (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); IZRC20(zrc20).approve(zrc20, gasFee); - IZRC20(zrc20).withdraw(abi.encodePacked(msg.sender), amount - 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; lastStakeTime[msg.sender] = block.timestamp; From d94d08d560169fdd4f0ecdb1103e4ae1a8af53e9 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 4 Sep 2023 13:05:42 +0900 Subject: [PATCH 02/27] btc in deploy script --- omnichain/staking/tasks/deploy.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/omnichain/staking/tasks/deploy.ts b/omnichain/staking/tasks/deploy.ts index ca121804..3e67a936 100644 --- a/omnichain/staking/tasks/deploy.ts +++ b/omnichain/staking/tasks/deploy.ts @@ -22,9 +22,14 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const factory = await hre.ethers.getContractFactory("Staking"); - const chainID = hre.config.networks[args.chain]?.chainId; - if (chainID === undefined) { - throw new Error(`🚨 Chain ${args.chain} not found in hardhat config.`); + let chainID; + if (args.chain === "btc_testnet") { + chainID = 18332; + } else { + chainID = hre.config.networks[args.chain]?.chainId; + if (chainID === undefined) { + throw new Error(`🚨 Chain ${args.chain} not found in hardhat config.`); + } } const ZRC20Address = getAddress("zrc20", args.chain); @@ -50,4 +55,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { `); }; -task("deploy", "Deploy the contract", main).addParam("chain", "Chain name"); +task("deploy", "Deploy the contract", main).addParam( + "chain", + "Chain ID (use btc_testnet for Bitcoin Testnet)" +); From eb6bb86696c21c8f2ecc2dd807caa015fabd70fd Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 4 Sep 2023 13:10:33 +0900 Subject: [PATCH 03/27] refactor deploy script --- omnichain/staking/tasks/deploy.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/omnichain/staking/tasks/deploy.ts b/omnichain/staking/tasks/deploy.ts index 3e67a936..a2afbcd9 100644 --- a/omnichain/staking/tasks/deploy.ts +++ b/omnichain/staking/tasks/deploy.ts @@ -22,25 +22,20 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const factory = await hre.ethers.getContractFactory("Staking"); - let chainID; + let symbol, chainID; if (args.chain === "btc_testnet") { + symbol = "BTC"; chainID = 18332; } else { + const zrc20 = getAddress("zrc20", args.chain); + const contract = new hre.ethers.Contract(zrc20, ZRC20.abi, signer); + symbol = await contract.symbol(); chainID = hre.config.networks[args.chain]?.chainId; if (chainID === undefined) { throw new Error(`🚨 Chain ${args.chain} not found in hardhat config.`); } } - const ZRC20Address = getAddress("zrc20", args.chain); - const ZRC20Contract = new hre.ethers.Contract( - ZRC20Address, - ZRC20.abi, - signer - ); - - const symbol = await ZRC20Contract.symbol(); - const contract = await factory.deploy( `Staking rewards for ${symbol}`, `R${symbol.toUpperCase()}`, From b507eebb87ac2eb7e064560ec16656b95dc26f16 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 8 Sep 2023 18:59:47 +0900 Subject: [PATCH 04/27] fix Bitcoin chain ID --- omnichain/staking/contracts/Staking.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index ca224c34..e59c4dfe 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -45,7 +45,7 @@ contract Staking is ERC20, zContract { address staker = BytesHelperLib.bytesToAddress(context.origin, 0); address beneficiary; - if (context.chainID == 18832) { + if (context.chainID == 18332) { beneficiary = BytesHelperLib.bytesToAddress(message, 0); } else { beneficiary = abi.decode(message, (address)); From 7ff9eedecd85dc5b4fe4e227d9a4f6af3ec3875e Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 11 Sep 2023 10:08:59 +0800 Subject: [PATCH 05/27] wip --- omnichain/staking/contracts/Staking.sol | 40 +- omnichain/staking/flat | 924 ++++++++++++++++++++++++ 2 files changed, 955 insertions(+), 9 deletions(-) create mode 100644 omnichain/staking/flat diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index e59c4dfe..42f42844 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -19,6 +19,14 @@ contract Staking is ERC20, zContract { 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_, @@ -60,43 +68,54 @@ contract Staking is ERC20, zContract { 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; + 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 { - if (beneficiaries[staker] != msg.sender) { - revert NotAuthorizedToClaim(); - } + 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 { - updateRewards(msg.sender); - require(stakes[msg.sender] >= amount, "Insufficient staked balance"); - address zrc20 = systemContract.gasCoinZRC20ByChainId(chainID); + 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) @@ -106,9 +125,12 @@ contract Staking is ERC20, zContract { } 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) { diff --git a/omnichain/staking/flat b/omnichain/staking/flat new file mode 100644 index 00000000..5ec2bf9e --- /dev/null +++ b/omnichain/staking/flat @@ -0,0 +1,924 @@ +// 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; + } +} From 7326c39babb917a67dcab03451c559bd237d11d3 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 10:40:39 +0800 Subject: [PATCH 06/27] remove comments --- omnichain/staking/contracts/Staking.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 42f42844..30d56f28 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -68,7 +68,7 @@ contract Staking is ERC20, zContract { uint256 amount ) internal { stakes[staker] += amount; - require(stakes[staker] >= amount, "Overflow detected"); // Check for overflows + require(stakes[staker] >= amount, "Overflow detected"); if (beneficiaries[staker] == address(0)) { beneficiaries[staker] = beneficiary; @@ -77,13 +77,13 @@ contract Staking is ERC20, zContract { lastStakeTime[staker] = block.timestamp; updateRewards(staker); - emit Staked(staker, beneficiary, amount); // Emitting Staked event + emit Staked(staker, beneficiary, amount); } 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 + require(rewardAmount >= timeDifference, "Overflow detected"); _mint(beneficiaries[staker], rewardAmount); lastStakeTime[staker] = block.timestamp; @@ -100,7 +100,7 @@ contract Staking is ERC20, zContract { updateRewards(staker); - emit RewardsClaimed(staker, rewardAmount); // Emitting RewardsClaimed event + emit RewardsClaimed(staker, rewardAmount); } function unstakeZRC(uint256 amount) external { @@ -126,11 +126,11 @@ contract Staking is ERC20, zContract { IZRC20(zrc20).withdraw(recipient, amount - gasFee); stakes[msg.sender] -= amount; - require(stakes[msg.sender] <= amount, "Underflow detected"); // Check for underflows + require(stakes[msg.sender] <= amount, "Underflow detected"); lastStakeTime[msg.sender] = block.timestamp; - emit Unstaked(msg.sender, amount); // Emitting Unstaked event + emit Unstaked(msg.sender, amount); } function queryRewards(address account) public view returns (uint256) { From 62562dc3125e6e1ea3711e95789a548c7654bb7f Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 11:43:48 +0800 Subject: [PATCH 07/27] fix tasks --- omnichain/staking/lib/convertToHexAddress.ts | 13 +++++++++++++ omnichain/staking/tasks/claim.ts | 6 +++++- omnichain/staking/tasks/rewards.ts | 5 ++++- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 omnichain/staking/lib/convertToHexAddress.ts diff --git a/omnichain/staking/lib/convertToHexAddress.ts b/omnichain/staking/lib/convertToHexAddress.ts new file mode 100644 index 00000000..f4f48bbe --- /dev/null +++ b/omnichain/staking/lib/convertToHexAddress.ts @@ -0,0 +1,13 @@ +import { ethers } from "ethers"; + +export const convertToHexAddress = (address: string): string => { + let addr: string; + try { + // Check if it's a valid hex address + addr = ethers.utils.getAddress(address); + } catch (e) { + // If not, try to convert it to an address from bech32 + addr = ("0x" + Buffer.from(address).toString("hex")).slice(0, 42); + } + return addr; +}; diff --git a/omnichain/staking/tasks/claim.ts b/omnichain/staking/tasks/claim.ts index cb953968..0c76cb3a 100644 --- a/omnichain/staking/tasks/claim.ts +++ b/omnichain/staking/tasks/claim.ts @@ -1,14 +1,18 @@ import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { convertToHexAddress } from "../lib/convertToHexAddress"; const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const [signer] = await hre.ethers.getSigners(); console.log(`🔑 Using account: ${signer.address}\n`); + const staker = convertToHexAddress(args.staker); + console.log(staker); + const factory = await hre.ethers.getContractFactory("Staking"); const contract = factory.attach(args.contract); - const tx = await contract.claimRewards(args.staker); + const tx = await contract.claimRewards(staker); const receipt = await tx.wait(); diff --git a/omnichain/staking/tasks/rewards.ts b/omnichain/staking/tasks/rewards.ts index 8feb2809..16d03fef 100644 --- a/omnichain/staking/tasks/rewards.ts +++ b/omnichain/staking/tasks/rewards.ts @@ -1,14 +1,17 @@ import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { convertToHexAddress } from "../lib/convertToHexAddress"; const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const [signer] = await hre.ethers.getSigners(); console.log(`🔑 Using account: ${signer.address}\n`); + const staker = convertToHexAddress(args.staker); + const factory = await hre.ethers.getContractFactory("Staking"); const contract = factory.attach(args.contract); - console.log(await contract.queryRewards(args.staker)); + console.log(await contract.queryRewards(staker)); }; task("rewards", "Query staking rewards", main) From 8dd15090e483826264d7608f612a3ef99930836c Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 12:36:19 +0800 Subject: [PATCH 08/27] 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; - } -} From bb3a9299c5248669a5e7811ee36f4775889344cc Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 17:08:08 +0800 Subject: [PATCH 09/27] wip --- omnichain/staking/contracts/Staking.sol | 10 +++++----- omnichain/staking/tasks/claim.ts | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 5e230d0f..14ab5057 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -65,7 +65,7 @@ contract Staking is ERC20, zContract { if (action == 1) { stakeZRC(staker, beneficiary, amount); } else if (action == 2) { - unstakeZRC(staker, amount); + unstakeZRC(staker); } else { revert UnknownAction(); } @@ -112,13 +112,13 @@ contract Staking is ERC20, zContract { emit RewardsClaimed(staker, rewardAmount); } - function unstakeZRC(address staker, uint256 amount) internal { - require(stakes[staker] >= amount, "Insufficient staked balance"); + function unstakeZRC(address staker) internal { + uint256 amount = stakes[staker]; updateRewards(staker); address zrc20 = systemContract.gasCoinZRC20ByChainId(chainID); - (address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); + (, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); require(amount >= gasFee, "Amount should be greater than the gas fee"); @@ -132,7 +132,7 @@ contract Staking is ERC20, zContract { } IZRC20(zrc20).withdraw(recipient, amount - gasFee); - stakes[staker] -= amount; + stakes[staker] = 0; require(stakes[staker] <= amount, "Underflow detected"); lastStakeTime[staker] = block.timestamp; diff --git a/omnichain/staking/tasks/claim.ts b/omnichain/staking/tasks/claim.ts index 0c76cb3a..499c7bcb 100644 --- a/omnichain/staking/tasks/claim.ts +++ b/omnichain/staking/tasks/claim.ts @@ -7,7 +7,6 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { console.log(`🔑 Using account: ${signer.address}\n`); const staker = convertToHexAddress(args.staker); - console.log(staker); const factory = await hre.ethers.getContractFactory("Staking"); const contract = factory.attach(args.contract); From 541506f0a7ae6ad05e5718675df813ef1ab0e8bf Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 12 Sep 2023 21:52:29 +0800 Subject: [PATCH 10/27] wip --- omnichain/staking/contracts/Staking.sol | 71 ++++++++++++++++++------- omnichain/staking/tasks/rewards.ts | 18 ++++++- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 14ab5057..ca5663db 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -15,18 +15,25 @@ contract Staking is ERC20, zContract { SystemContract public immutable systemContract; uint256 public immutable chainID; - mapping(address => uint256) public stakes; - mapping(address => address) public beneficiaries; - mapping(address => uint256) public lastStakeTime; + mapping(bytes => uint256) public stakes; + mapping(bytes => address) public beneficiaries; + mapping(bytes => uint256) public lastStakeTime; uint256 public rewardRate = 1; event Staked( - address indexed staker, + bytes indexed staker, address indexed beneficiary, uint256 amount ); - event RewardsClaimed(address indexed staker, uint256 rewardAmount); - event Unstaked(address indexed staker, uint256 amount); + event RewardsClaimed(bytes indexed staker, uint256 rewardAmount); + event Unstaked(bytes indexed staker, uint256 amount); + event OnCrossChainCallEvent( + bytes staker, + address beneficiary, + uint32 action + ); + + event Logging(string); constructor( string memory name_, @@ -38,6 +45,27 @@ contract Staking is ERC20, zContract { chainID = chainID_; } + function decode( + bytes memory message + ) public pure returns (bytes memory bech32, address hexAddr, uint32 value) { + require(message.length == 66, "Invalid message length"); // 42 (bech32) + 20 (address) + 4 (uint32) + + bytes memory bech32Bytes = new bytes(42); + + for (uint256 i = 0; i < bech32Bytes.length; i++) { + bech32Bytes[i] = message[i]; + } + + bech32 = bech32Bytes; + + assembly { + hexAddr := mload(add(message, 42)) + value := mload(add(message, 62)) + } + + return (bech32, hexAddr, value); + } + function onCrossChainCall( zContext calldata context, address zrc20, @@ -48,20 +76,26 @@ contract Staking is ERC20, zContract { revert SenderNotSystemContract(); } + emit Logging("onCrossChainCall"); + address acceptedZRC20 = systemContract.gasCoinZRC20ByChainId(chainID); if (zrc20 != acceptedZRC20) revert WrongChain(); - address staker = BytesHelperLib.bytesToAddress(context.origin, 0); + bytes memory staker; address beneficiary; uint32 action; if (context.chainID == 18332) { - beneficiary = BytesHelperLib.bytesToAddress(message, 0); - action = BytesHelperLib.bytesToUint32(message, 20); + (staker, beneficiary, action) = decode(message); } else { - (beneficiary, action) = abi.decode(message, (address, uint32)); + (staker, beneficiary, action) = abi.decode( + message, + (bytes, address, uint32) + ); } + emit OnCrossChainCallEvent(staker, beneficiary, action); + if (action == 1) { stakeZRC(staker, beneficiary, amount); } else if (action == 2) { @@ -72,10 +106,11 @@ contract Staking is ERC20, zContract { } function stakeZRC( - address staker, + bytes memory staker, address beneficiary, uint256 amount ) internal { + emit Logging("stakeZRC"); stakes[staker] += amount; require(stakes[staker] >= amount, "Overflow detected"); @@ -89,7 +124,7 @@ contract Staking is ERC20, zContract { emit Staked(staker, beneficiary, amount); } - function updateRewards(address staker) internal { + function updateRewards(bytes memory staker) internal { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; require(rewardAmount >= timeDifference, "Overflow detected"); @@ -98,7 +133,7 @@ contract Staking is ERC20, zContract { lastStakeTime[staker] = block.timestamp; } - function claimRewards(address staker) external { + function claimRewards(bytes memory staker) external { require( beneficiaries[staker] == msg.sender, "Not authorized to claim rewards" @@ -112,7 +147,7 @@ contract Staking is ERC20, zContract { emit RewardsClaimed(staker, rewardAmount); } - function unstakeZRC(address staker) internal { + function unstakeZRC(bytes memory staker) internal { uint256 amount = stakes[staker]; updateRewards(staker); @@ -126,7 +161,7 @@ contract Staking is ERC20, zContract { bytes memory recipient; if (chainID == 18332) { - recipient = abi.encodePacked(BytesHelperLib.addressToBytes(staker)); + recipient = abi.encodePacked(staker); } else { recipient = abi.encodePacked(staker); } @@ -140,9 +175,9 @@ contract Staking is ERC20, zContract { emit Unstaked(staker, amount); } - function queryRewards(address account) public view returns (uint256) { - uint256 timeDifference = block.timestamp - lastStakeTime[account]; - uint256 rewardAmount = timeDifference * stakes[account] * rewardRate; + function queryRewards(bytes memory staker) public view returns (uint256) { + uint256 timeDifference = block.timestamp - lastStakeTime[staker]; + uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; return rewardAmount; } } diff --git a/omnichain/staking/tasks/rewards.ts b/omnichain/staking/tasks/rewards.ts index 16d03fef..941405c4 100644 --- a/omnichain/staking/tasks/rewards.ts +++ b/omnichain/staking/tasks/rewards.ts @@ -1,7 +1,23 @@ import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { convertToHexAddress } from "../lib/convertToHexAddress"; +const { decode } = require("bech32"); +const { arrayify } = require("@ethersproject/bytes"); +function bech32ToHex(bech32Address: string) { + const { words } = decode(bech32Address); + + // Convert 5-bit words to bytes + let bytes = []; + for (let i = 0; i < words.length; i++) { + let word = words[i]; + for (let j = 4; j >= 0; j--) { + bytes.push((word >> j) & 1); + } + } + + return arrayify(bytes); +} const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const [signer] = await hre.ethers.getSigners(); console.log(`🔑 Using account: ${signer.address}\n`); @@ -11,7 +27,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const factory = await hre.ethers.getContractFactory("Staking"); const contract = factory.attach(args.contract); - console.log(await contract.queryRewards(staker)); + console.log(await contract.queryRewards(bech32ToHex(args.staker))); }; task("rewards", "Query staking rewards", main) From e498bdb661d5b28ce5c740d993b09ac7cbb84863 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Mon, 18 Sep 2023 19:04:00 +0400 Subject: [PATCH 11/27] refactor --- omnichain/staking/contracts/Staking.sol | 118 ++++++++++-------------- 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index ca5663db..efb76860 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -16,24 +16,20 @@ contract Staking is ERC20, zContract { uint256 public immutable chainID; mapping(bytes => uint256) public stakes; + mapping(bytes => bytes) public withdraw; mapping(bytes => address) public beneficiaries; mapping(bytes => uint256) public lastStakeTime; uint256 public rewardRate = 1; - event Staked( - bytes indexed staker, - address indexed beneficiary, - uint256 amount - ); + event Staked(bytes indexed staker, uint256 amount); event RewardsClaimed(bytes indexed staker, uint256 rewardAmount); event Unstaked(bytes indexed staker, uint256 amount); - event OnCrossChainCallEvent( - bytes staker, - address beneficiary, - uint32 action - ); + event OnCrossChainCallEvent(bytes staker, uint32 action); - event Logging(string); + event StakeZRC(bytes indexed staker, uint256 amount); + event UnstakeZRC(bytes indexed staker); + event SetBeneficiary(bytes indexed staker, address beneficiaryAddress); + event SetWithdraw(bytes indexed staker, bytes withdrawAddress); constructor( string memory name_, @@ -45,25 +41,16 @@ contract Staking is ERC20, zContract { chainID = chainID_; } - function decode( - bytes memory message - ) public pure returns (bytes memory bech32, address hexAddr, uint32 value) { - require(message.length == 66, "Invalid message length"); // 42 (bech32) + 20 (address) + 4 (uint32) - + function bytesToBech32Bytes( + bytes memory data, + uint256 offset + ) public pure returns (bytes memory) { bytes memory bech32Bytes = new bytes(42); - - for (uint256 i = 0; i < bech32Bytes.length; i++) { - bech32Bytes[i] = message[i]; + for (uint i = 0; i < 42; i++) { + bech32Bytes[i] = data[i + offset]; } - bech32 = bech32Bytes; - - assembly { - hexAddr := mload(add(message, 42)) - value := mload(add(message, 62)) - } - - return (bech32, hexAddr, value); + return bech32Bytes; } function onCrossChainCall( @@ -72,56 +59,52 @@ contract Staking is ERC20, zContract { uint256 amount, bytes calldata message ) external override { - if (msg.sender != address(systemContract)) { - revert SenderNotSystemContract(); - } - - emit Logging("onCrossChainCall"); - - address acceptedZRC20 = systemContract.gasCoinZRC20ByChainId(chainID); - if (zrc20 != acceptedZRC20) revert WrongChain(); - - bytes memory staker; - address beneficiary; - uint32 action; - - if (context.chainID == 18332) { - (staker, beneficiary, action) = decode(message); - } else { - (staker, beneficiary, action) = abi.decode( - message, - (bytes, address, uint32) - ); - } - - emit OnCrossChainCallEvent(staker, beneficiary, action); + bytes memory withdrawAddress; + bytes memory stakerAddress; + address beneficiaryAddress; + uint8 action = uint8(message[0]); if (action == 1) { - stakeZRC(staker, beneficiary, amount); + emit StakeZRC(context.origin, amount); + stakeZRC(context.origin, amount); } else if (action == 2) { - unstakeZRC(staker); + emit UnstakeZRC(context.origin); + unstakeZRC(context.origin); + } else if (action == 3) { + beneficiaryAddress = BytesHelperLib.bytesToAddress(message, 1); + emit SetBeneficiary(context.origin, beneficiaryAddress); + setBeneficiary(context.origin, beneficiaryAddress); + } else if (action == 4) { + withdrawAddress = bytesToBech32Bytes(message, 1); + emit SetWithdraw(context.origin, withdrawAddress); + setWithdraw(context.origin, withdrawAddress); } else { revert UnknownAction(); } } - function stakeZRC( - bytes memory staker, - address beneficiary, - uint256 amount - ) internal { - emit Logging("stakeZRC"); - stakes[staker] += amount; - require(stakes[staker] >= amount, "Overflow detected"); + function stakeZRC(bytes memory stakerAddress, uint256 amount) internal { + stakes[stakerAddress] += amount; + require(stakes[stakerAddress] >= amount, "Overflow detected"); - if (beneficiaries[staker] == address(0)) { - beneficiaries[staker] = beneficiary; - } + lastStakeTime[stakerAddress] = block.timestamp; + updateRewards(stakerAddress); - lastStakeTime[staker] = block.timestamp; - updateRewards(staker); + emit Staked(stakerAddress, amount); + } + + function setBeneficiary( + bytes memory stakerAddress, + address beneficiaryAddress + ) internal { + beneficiaries[stakerAddress] = beneficiaryAddress; + } - emit Staked(staker, beneficiary, amount); + function setWithdraw( + bytes memory stakerAddress, + bytes memory withdrawAddress + ) internal { + withdraw[stakerAddress] = withdrawAddress; } function updateRewards(bytes memory staker) internal { @@ -138,12 +121,9 @@ contract Staking is ERC20, zContract { 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); } From bc0da61b0b2a5bfed86643341fccad729e769507 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 19 Sep 2023 09:22:18 +0400 Subject: [PATCH 12/27] wip --- omnichain/staking/contracts/Staking.sol | 10 +++++----- omnichain/staking/hardhat.config.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index efb76860..cab5ba22 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -83,7 +83,7 @@ contract Staking is ERC20, zContract { } } - function stakeZRC(bytes memory stakerAddress, uint256 amount) internal { + function stakeZRC(bytes memory stakerAddress, uint256 amount) public { stakes[stakerAddress] += amount; require(stakes[stakerAddress] >= amount, "Overflow detected"); @@ -96,18 +96,18 @@ contract Staking is ERC20, zContract { function setBeneficiary( bytes memory stakerAddress, address beneficiaryAddress - ) internal { + ) public { beneficiaries[stakerAddress] = beneficiaryAddress; } function setWithdraw( bytes memory stakerAddress, bytes memory withdrawAddress - ) internal { + ) public { withdraw[stakerAddress] = withdrawAddress; } - function updateRewards(bytes memory staker) internal { + function updateRewards(bytes memory staker) public { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; require(rewardAmount >= timeDifference, "Overflow detected"); @@ -127,7 +127,7 @@ contract Staking is ERC20, zContract { emit RewardsClaimed(staker, rewardAmount); } - function unstakeZRC(bytes memory staker) internal { + function unstakeZRC(bytes memory staker) public { uint256 amount = stakes[staker]; updateRewards(staker); diff --git a/omnichain/staking/hardhat.config.ts b/omnichain/staking/hardhat.config.ts index 53d824c5..82daf9cd 100644 --- a/omnichain/staking/hardhat.config.ts +++ b/omnichain/staking/hardhat.config.ts @@ -3,6 +3,7 @@ import "./tasks/deploy"; import "./tasks/rewards"; import "./tasks/claim"; import "./tasks/unstake"; +import "./tasks/play"; import "@nomicfoundation/hardhat-toolbox"; import "@zetachain/toolkit/tasks"; From e7835349efba1c1b8e7f0d4ebb93ce0949e4ed91 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 19 Sep 2023 17:05:06 +0400 Subject: [PATCH 13/27] bitcoin support --- omnichain/staking/contracts/Staking.sol | 68 ++++++++++++------------- omnichain/staking/hardhat.config.ts | 1 - 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index cab5ba22..5d6f83d3 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -15,21 +15,21 @@ contract Staking is ERC20, zContract { SystemContract public immutable systemContract; uint256 public immutable chainID; - mapping(bytes => uint256) public stakes; - mapping(bytes => bytes) public withdraw; - mapping(bytes => address) public beneficiaries; - mapping(bytes => uint256) public lastStakeTime; + mapping(address => uint256) public stakes; + mapping(address => bytes) public withdraw; + mapping(address => address) public beneficiaries; + mapping(address => uint256) public lastStakeTime; uint256 public rewardRate = 1; - event Staked(bytes indexed staker, uint256 amount); - event RewardsClaimed(bytes indexed staker, uint256 rewardAmount); - event Unstaked(bytes indexed staker, uint256 amount); - event OnCrossChainCallEvent(bytes staker, uint32 action); + event Staked(address indexed staker, uint256 amount); + event RewardsClaimed(address indexed staker, uint256 rewardAmount); + event Unstaked(address indexed staker, uint256 amount); + event OnCrossChainCallEvent(address staker, uint32 action); - event StakeZRC(bytes indexed staker, uint256 amount); - event UnstakeZRC(bytes indexed staker); - event SetBeneficiary(bytes indexed staker, address beneficiaryAddress); - event SetWithdraw(bytes indexed staker, bytes withdrawAddress); + event StakeZRC(address indexed staker, uint256 amount); + event UnstakeZRC(address indexed staker); + event SetBeneficiary(address indexed staker, address beneficiaryAddress); + event SetWithdraw(address indexed staker, bytes withdrawAddress); constructor( string memory name_, @@ -42,7 +42,7 @@ contract Staking is ERC20, zContract { } function bytesToBech32Bytes( - bytes memory data, + bytes calldata data, uint256 offset ) public pure returns (bytes memory) { bytes memory bech32Bytes = new bytes(42); @@ -60,30 +60,31 @@ contract Staking is ERC20, zContract { bytes calldata message ) external override { bytes memory withdrawAddress; - bytes memory stakerAddress; + address stakerAddress = BytesHelperLib.bytesToAddress( + context.origin, + 0 + ); address beneficiaryAddress; uint8 action = uint8(message[0]); if (action == 1) { - emit StakeZRC(context.origin, amount); - stakeZRC(context.origin, amount); + emit StakeZRC(stakerAddress, amount); + stakeZRC(stakerAddress, amount); } else if (action == 2) { - emit UnstakeZRC(context.origin); - unstakeZRC(context.origin); + unstakeZRC(stakerAddress); } else if (action == 3) { beneficiaryAddress = BytesHelperLib.bytesToAddress(message, 1); - emit SetBeneficiary(context.origin, beneficiaryAddress); - setBeneficiary(context.origin, beneficiaryAddress); + setBeneficiary(stakerAddress, beneficiaryAddress); } else if (action == 4) { withdrawAddress = bytesToBech32Bytes(message, 1); - emit SetWithdraw(context.origin, withdrawAddress); - setWithdraw(context.origin, withdrawAddress); + setWithdraw(stakerAddress, withdrawAddress); } else { revert UnknownAction(); } } - function stakeZRC(bytes memory stakerAddress, uint256 amount) public { + function stakeZRC(address stakerAddress, uint256 amount) public { + emit StakeZRC(stakerAddress, amount); stakes[stakerAddress] += amount; require(stakes[stakerAddress] >= amount, "Overflow detected"); @@ -94,20 +95,22 @@ contract Staking is ERC20, zContract { } function setBeneficiary( - bytes memory stakerAddress, + address stakerAddress, address beneficiaryAddress ) public { beneficiaries[stakerAddress] = beneficiaryAddress; + emit SetBeneficiary(stakerAddress, beneficiaryAddress); } function setWithdraw( - bytes memory stakerAddress, + address stakerAddress, bytes memory withdrawAddress ) public { withdraw[stakerAddress] = withdrawAddress; + emit SetWithdraw(stakerAddress, withdrawAddress); } - function updateRewards(bytes memory staker) public { + function updateRewards(address staker) public { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; require(rewardAmount >= timeDifference, "Overflow detected"); @@ -116,7 +119,7 @@ contract Staking is ERC20, zContract { lastStakeTime[staker] = block.timestamp; } - function claimRewards(bytes memory staker) external { + function claimRewards(address staker) external { require( beneficiaries[staker] == msg.sender, "Not authorized to claim rewards" @@ -127,7 +130,7 @@ contract Staking is ERC20, zContract { emit RewardsClaimed(staker, rewardAmount); } - function unstakeZRC(bytes memory staker) public { + function unstakeZRC(address staker) public { uint256 amount = stakes[staker]; updateRewards(staker); @@ -139,12 +142,7 @@ contract Staking is ERC20, zContract { IZRC20(zrc20).approve(zrc20, gasFee); - bytes memory recipient; - if (chainID == 18332) { - recipient = abi.encodePacked(staker); - } else { - recipient = abi.encodePacked(staker); - } + bytes memory recipient = withdraw[staker]; IZRC20(zrc20).withdraw(recipient, amount - gasFee); stakes[staker] = 0; @@ -155,7 +153,7 @@ contract Staking is ERC20, zContract { emit Unstaked(staker, amount); } - function queryRewards(bytes memory staker) public view returns (uint256) { + function queryRewards(address staker) public view returns (uint256) { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; return rewardAmount; diff --git a/omnichain/staking/hardhat.config.ts b/omnichain/staking/hardhat.config.ts index 82daf9cd..53d824c5 100644 --- a/omnichain/staking/hardhat.config.ts +++ b/omnichain/staking/hardhat.config.ts @@ -3,7 +3,6 @@ import "./tasks/deploy"; import "./tasks/rewards"; import "./tasks/claim"; import "./tasks/unstake"; -import "./tasks/play"; import "@nomicfoundation/hardhat-toolbox"; import "@zetachain/toolkit/tasks"; From 784f14345c835977a7dea77de28c86a2e51df1b7 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 19 Sep 2023 17:07:32 +0400 Subject: [PATCH 14/27] refactor --- omnichain/staking/contracts/Staking.sol | 51 ++++++++++--------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 5d6f83d3..d09b3f29 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -60,54 +60,45 @@ contract Staking is ERC20, zContract { bytes calldata message ) external override { bytes memory withdrawAddress; - address stakerAddress = BytesHelperLib.bytesToAddress( - context.origin, - 0 - ); - address beneficiaryAddress; + address staker = BytesHelperLib.bytesToAddress(context.origin, 0); + address beneficiary; uint8 action = uint8(message[0]); if (action == 1) { - emit StakeZRC(stakerAddress, amount); - stakeZRC(stakerAddress, amount); + emit StakeZRC(staker, amount); + stakeZRC(staker, amount); } else if (action == 2) { - unstakeZRC(stakerAddress); + unstakeZRC(staker); } else if (action == 3) { - beneficiaryAddress = BytesHelperLib.bytesToAddress(message, 1); - setBeneficiary(stakerAddress, beneficiaryAddress); + beneficiary = BytesHelperLib.bytesToAddress(message, 1); + setBeneficiary(staker, beneficiary); } else if (action == 4) { withdrawAddress = bytesToBech32Bytes(message, 1); - setWithdraw(stakerAddress, withdrawAddress); + setWithdraw(staker, withdrawAddress); } else { revert UnknownAction(); } } - function stakeZRC(address stakerAddress, uint256 amount) public { - emit StakeZRC(stakerAddress, amount); - stakes[stakerAddress] += amount; - require(stakes[stakerAddress] >= amount, "Overflow detected"); + function stakeZRC(address staker, uint256 amount) public { + emit StakeZRC(staker, amount); + stakes[staker] += amount; + require(stakes[staker] >= amount, "Overflow detected"); - lastStakeTime[stakerAddress] = block.timestamp; - updateRewards(stakerAddress); + lastStakeTime[staker] = block.timestamp; + updateRewards(staker); - emit Staked(stakerAddress, amount); + emit Staked(staker, amount); } - function setBeneficiary( - address stakerAddress, - address beneficiaryAddress - ) public { - beneficiaries[stakerAddress] = beneficiaryAddress; - emit SetBeneficiary(stakerAddress, beneficiaryAddress); + function setBeneficiary(address staker, address beneficiaryAddress) public { + beneficiaries[staker] = beneficiaryAddress; + emit SetBeneficiary(staker, beneficiaryAddress); } - function setWithdraw( - address stakerAddress, - bytes memory withdrawAddress - ) public { - withdraw[stakerAddress] = withdrawAddress; - emit SetWithdraw(stakerAddress, withdrawAddress); + function setWithdraw(address staker, bytes memory withdrawAddress) public { + withdraw[staker] = withdrawAddress; + emit SetWithdraw(staker, withdrawAddress); } function updateRewards(address staker) public { From f5a6576bd3078dbf6785fa5c307b96121898e65f Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 19 Sep 2023 21:05:42 +0400 Subject: [PATCH 15/27] multichain support --- omnichain/staking/contracts/Staking.sol | 29 +++++++++++-------- omnichain/staking/hardhat.config.ts | 5 +++- .../tasks/{interact.ts => beneficiary.ts} | 11 ++++--- omnichain/staking/tasks/stake.ts | 25 ++++++++++++++++ omnichain/staking/tasks/unstake.ts | 20 +++++++------ omnichain/staking/tasks/withdraw.ts | 25 ++++++++++++++++ 6 files changed, 89 insertions(+), 26 deletions(-) rename omnichain/staking/tasks/{interact.ts => beneficiary.ts} (81%) create mode 100644 omnichain/staking/tasks/stake.ts create mode 100644 omnichain/staking/tasks/withdraw.ts diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index d09b3f29..c1c31f0a 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -62,18 +62,30 @@ contract Staking is ERC20, zContract { bytes memory withdrawAddress; address staker = BytesHelperLib.bytesToAddress(context.origin, 0); address beneficiary; - uint8 action = uint8(message[0]); + uint8 action; + if (chainID == 18332) { + action = uint8(message[0]); + } else { + action = abi.decode(message, (uint8)); + } if (action == 1) { - emit StakeZRC(staker, amount); stakeZRC(staker, amount); } else if (action == 2) { unstakeZRC(staker); } else if (action == 3) { - beneficiary = BytesHelperLib.bytesToAddress(message, 1); + if (chainID == 18332) { + beneficiary = BytesHelperLib.bytesToAddress(message, 1); + } else { + (, beneficiary) = abi.decode(message, (uint8, address)); + } setBeneficiary(staker, beneficiary); } else if (action == 4) { - withdrawAddress = bytesToBech32Bytes(message, 1); + if (chainID == 18332) { + withdrawAddress = bytesToBech32Bytes(message, 1); + } else { + withdrawAddress = context.origin; + } setWithdraw(staker, withdrawAddress); } else { revert UnknownAction(); @@ -81,24 +93,19 @@ contract Staking is ERC20, zContract { } function stakeZRC(address staker, uint256 amount) public { - emit StakeZRC(staker, amount); stakes[staker] += amount; require(stakes[staker] >= amount, "Overflow detected"); lastStakeTime[staker] = block.timestamp; updateRewards(staker); - - emit Staked(staker, amount); } function setBeneficiary(address staker, address beneficiaryAddress) public { beneficiaries[staker] = beneficiaryAddress; - emit SetBeneficiary(staker, beneficiaryAddress); } function setWithdraw(address staker, bytes memory withdrawAddress) public { withdraw[staker] = withdrawAddress; - emit SetWithdraw(staker, withdrawAddress); } function updateRewards(address staker) public { @@ -118,7 +125,6 @@ contract Staking is ERC20, zContract { uint256 rewardAmount = queryRewards(staker); require(rewardAmount > 0, "No rewards to claim"); updateRewards(staker); - emit RewardsClaimed(staker, rewardAmount); } function unstakeZRC(address staker) public { @@ -136,12 +142,11 @@ contract Staking is ERC20, zContract { bytes memory recipient = withdraw[staker]; IZRC20(zrc20).withdraw(recipient, amount - gasFee); + stakes[staker] = 0; require(stakes[staker] <= amount, "Underflow detected"); lastStakeTime[staker] = block.timestamp; - - emit Unstaked(staker, amount); } function queryRewards(address staker) public view returns (uint256) { diff --git a/omnichain/staking/hardhat.config.ts b/omnichain/staking/hardhat.config.ts index 53d824c5..caca0f35 100644 --- a/omnichain/staking/hardhat.config.ts +++ b/omnichain/staking/hardhat.config.ts @@ -1,8 +1,11 @@ -import "./tasks/interact"; +import "./tasks/stake"; import "./tasks/deploy"; import "./tasks/rewards"; import "./tasks/claim"; import "./tasks/unstake"; +import "./tasks/beneficiary"; +import "./tasks/withdraw"; +import "./tasks/unstake"; import "@nomicfoundation/hardhat-toolbox"; import "@zetachain/toolkit/tasks"; diff --git a/omnichain/staking/tasks/interact.ts b/omnichain/staking/tasks/beneficiary.ts similarity index 81% rename from omnichain/staking/tasks/interact.ts rename to omnichain/staking/tasks/beneficiary.ts index d714d065..de652c64 100644 --- a/omnichain/staking/tasks/interact.ts +++ b/omnichain/staking/tasks/beneficiary.ts @@ -8,12 +8,15 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const [signer] = await hre.ethers.getSigners(); console.log(`🔑 Using account: ${signer.address}\n`); - const data = prepareData(args.contract, ["address"], [args.beneficiary]); + const data = prepareData( + args.contract, + ["uint8", "address"], + ["3", args.beneficiary] + ); const to = getAddress("tss", hre.network.name); const value = parseEther(args.amount); const tx = await signer.sendTransaction({ data, to, value }); - console.log(` 🚀 Successfully broadcasted a token transfer transaction on ${hre.network.name} network. 📝 Transaction hash: ${tx.hash} @@ -21,7 +24,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await trackCCTX(tx.hash); }; -task("interact", "Interact with the contract", main) +task("beneficiary", "", main) .addParam("contract", "The address of the withdraw contract on ZetaChain") .addParam("amount", "Amount of tokens to send") - .addParam("beneficiary"); + .addPositionalParam("beneficiary", "The address of the beneficiary"); diff --git a/omnichain/staking/tasks/stake.ts b/omnichain/staking/tasks/stake.ts new file mode 100644 index 00000000..7bcec388 --- /dev/null +++ b/omnichain/staking/tasks/stake.ts @@ -0,0 +1,25 @@ +import { task } from "hardhat/config"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { parseEther } from "@ethersproject/units"; +import { getAddress } from "@zetachain/protocol-contracts"; +import { prepareData, trackCCTX } from "@zetachain/toolkit/helpers"; + +const main = async (args: any, hre: HardhatRuntimeEnvironment) => { + const [signer] = await hre.ethers.getSigners(); + console.log(`🔑 Using account: ${signer.address}\n`); + + const data = prepareData(args.contract, ["uint8"], ["1"]); + const to = getAddress("tss", hre.network.name); + const value = parseEther(args.amount); + + const tx = await signer.sendTransaction({ data, to, value }); + console.log(` +🚀 Successfully broadcasted a token transfer transaction on ${hre.network.name} network. +📝 Transaction hash: ${tx.hash} +`); + await trackCCTX(tx.hash); +}; + +task("stake", "Interact with the contract", main) + .addParam("contract", "The address of the withdraw contract on ZetaChain") + .addParam("amount", "Amount of tokens to send"); diff --git a/omnichain/staking/tasks/unstake.ts b/omnichain/staking/tasks/unstake.ts index ad66372c..8f7ba76b 100644 --- a/omnichain/staking/tasks/unstake.ts +++ b/omnichain/staking/tasks/unstake.ts @@ -1,21 +1,23 @@ import { task } from "hardhat/config"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { parseEther } from "@ethersproject/units"; +import { getAddress } from "@zetachain/protocol-contracts"; +import { prepareData, trackCCTX } from "@zetachain/toolkit/helpers"; const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const [signer] = await hre.ethers.getSigners(); console.log(`🔑 Using account: ${signer.address}\n`); - const factory = await hre.ethers.getContractFactory("Staking"); - const contract = factory.attach(args.contract); + const data = prepareData(args.contract, ["uint8"], ["2"]); + const to = getAddress("tss", hre.network.name); + const value = parseEther(args.amount); - const amount = parseEther(args.amount); - - const tx = await contract.unstakeZRC(amount); - - const receipt = await tx.wait(); - - console.log(receipt); + const tx = await signer.sendTransaction({ data, to, value }); + console.log(` +🚀 Successfully broadcasted a token transfer transaction on ${hre.network.name} network. +📝 Transaction hash: ${tx.hash} +`); + await trackCCTX(tx.hash); }; task("unstake", "Unstake tokens", main) diff --git a/omnichain/staking/tasks/withdraw.ts b/omnichain/staking/tasks/withdraw.ts new file mode 100644 index 00000000..42e56fe5 --- /dev/null +++ b/omnichain/staking/tasks/withdraw.ts @@ -0,0 +1,25 @@ +import { task } from "hardhat/config"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { parseEther } from "@ethersproject/units"; +import { getAddress } from "@zetachain/protocol-contracts"; +import { prepareData, trackCCTX } from "@zetachain/toolkit/helpers"; + +const main = async (args: any, hre: HardhatRuntimeEnvironment) => { + const [signer] = await hre.ethers.getSigners(); + console.log(`🔑 Using account: ${signer.address}\n`); + + const data = prepareData(args.contract, ["uint8"], ["4"]); + const to = getAddress("tss", hre.network.name); + const value = parseEther(args.amount); + + const tx = await signer.sendTransaction({ data, to, value }); + console.log(` +🚀 Successfully broadcasted a token transfer transaction on ${hre.network.name} network. +📝 Transaction hash: ${tx.hash} +`); + await trackCCTX(tx.hash); +}; + +task("withdraw", "", main) + .addParam("contract", "The address of the withdraw contract on ZetaChain") + .addParam("amount", "Amount of tokens to send"); From 5e56ffd039af4920414e1acf6b5c3d3426139326 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Tue, 19 Sep 2023 21:20:39 +0400 Subject: [PATCH 16/27] wip --- omnichain/staking/contracts/Staking.sol | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index c1c31f0a..8e644ee1 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -79,14 +79,14 @@ contract Staking is ERC20, zContract { } else { (, beneficiary) = abi.decode(message, (uint8, address)); } - setBeneficiary(staker, beneficiary); + beneficiaries[staker] = beneficiary; } else if (action == 4) { if (chainID == 18332) { withdrawAddress = bytesToBech32Bytes(message, 1); } else { withdrawAddress = context.origin; } - setWithdraw(staker, withdrawAddress); + withdraw[staker] = withdrawAddress; } else { revert UnknownAction(); } @@ -100,14 +100,6 @@ contract Staking is ERC20, zContract { updateRewards(staker); } - function setBeneficiary(address staker, address beneficiaryAddress) public { - beneficiaries[staker] = beneficiaryAddress; - } - - function setWithdraw(address staker, bytes memory withdrawAddress) public { - withdraw[staker] = withdrawAddress; - } - function updateRewards(address staker) public { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; From ff6eeeb92c8002c83b12a4d04d4b3955ef074cba Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Wed, 20 Sep 2023 13:14:36 +0400 Subject: [PATCH 17/27] refactor complete --- omnichain/staking/contracts/Staking.sol | 105 ++--- omnichain/staking/hardhat.config.ts | 2 +- omnichain/staking/package.json | 4 +- omnichain/staking/tasks/address.ts | 18 + omnichain/staking/tasks/beneficiary.ts | 8 +- omnichain/staking/tasks/rewards.ts | 35 -- omnichain/staking/tasks/stake.ts | 4 +- omnichain/staking/tasks/withdraw.ts | 8 +- omnichain/staking/yarn.lock | 501 ++++++++++++++++++++++-- 9 files changed, 553 insertions(+), 132 deletions(-) create mode 100644 omnichain/staking/tasks/address.ts delete mode 100644 omnichain/staking/tasks/rewards.ts diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 8e644ee1..5c63fb10 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -9,27 +9,17 @@ import "@zetachain/toolkit/contracts/BytesHelperLib.sol"; contract Staking is ERC20, zContract { error SenderNotSystemContract(); error WrongChain(); - error NotAuthorizedToClaim(); error UnknownAction(); SystemContract public immutable systemContract; uint256 public immutable chainID; + uint256 public rewardRate = 1; + uint256 constant BITCOIN = 18332; mapping(address => uint256) public stakes; - mapping(address => bytes) public withdraw; + mapping(address => bytes) public withdrawAddresses; mapping(address => address) public beneficiaries; mapping(address => uint256) public lastStakeTime; - uint256 public rewardRate = 1; - - event Staked(address indexed staker, uint256 amount); - event RewardsClaimed(address indexed staker, uint256 rewardAmount); - event Unstaked(address indexed staker, uint256 amount); - event OnCrossChainCallEvent(address staker, uint32 action); - - event StakeZRC(address indexed staker, uint256 amount); - event UnstakeZRC(address indexed staker); - event SetBeneficiary(address indexed staker, address beneficiaryAddress); - event SetWithdraw(address indexed staker, bytes withdrawAddress); constructor( string memory name_, @@ -59,40 +49,34 @@ contract Staking is ERC20, zContract { uint256 amount, bytes calldata message ) external override { - bytes memory withdrawAddress; - address staker = BytesHelperLib.bytesToAddress(context.origin, 0); - address beneficiary; - uint8 action; - if (chainID == 18332) { - action = uint8(message[0]); - } else { - action = abi.decode(message, (uint8)); + if (msg.sender != address(systemContract)) { + revert SenderNotSystemContract(); + } + + if (chainID != context.chainID) { + revert WrongChain(); } + address staker = BytesHelperLib.bytesToAddress(context.origin, 0); + + uint8 action = chainID == BITCOIN + ? uint8(message[0]) + : abi.decode(message, (uint8)); + if (action == 1) { stakeZRC(staker, amount); } else if (action == 2) { unstakeZRC(staker); } else if (action == 3) { - if (chainID == 18332) { - beneficiary = BytesHelperLib.bytesToAddress(message, 1); - } else { - (, beneficiary) = abi.decode(message, (uint8, address)); - } - beneficiaries[staker] = beneficiary; + setBeneficiary(staker, message); } else if (action == 4) { - if (chainID == 18332) { - withdrawAddress = bytesToBech32Bytes(message, 1); - } else { - withdrawAddress = context.origin; - } - withdraw[staker] = withdrawAddress; + setWithdraw(staker, message, context.origin); } else { revert UnknownAction(); } } - function stakeZRC(address staker, uint256 amount) public { + function stakeZRC(address staker, uint256 amount) internal { stakes[staker] += amount; require(stakes[staker] >= amount, "Overflow detected"); @@ -100,7 +84,7 @@ contract Staking is ERC20, zContract { updateRewards(staker); } - function updateRewards(address staker) public { + function updateRewards(address staker) internal { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; require(rewardAmount >= timeDifference, "Overflow detected"); @@ -109,17 +93,7 @@ contract Staking is ERC20, zContract { 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); - } - - function unstakeZRC(address staker) public { + function unstakeZRC(address staker) internal { uint256 amount = stakes[staker]; updateRewards(staker); @@ -129,10 +103,9 @@ contract Staking is ERC20, zContract { require(amount >= gasFee, "Amount should be greater than the gas fee"); - IZRC20(zrc20).approve(zrc20, gasFee); - - bytes memory recipient = withdraw[staker]; + bytes memory recipient = withdrawAddresses[staker]; + IZRC20(zrc20).approve(zrc20, gasFee); IZRC20(zrc20).withdraw(recipient, amount - gasFee); stakes[staker] = 0; @@ -141,9 +114,43 @@ contract Staking is ERC20, zContract { lastStakeTime[staker] = block.timestamp; } + function setBeneficiary(address staker, bytes calldata message) internal { + address beneficiary; + if (chainID == BITCOIN) { + beneficiary = BytesHelperLib.bytesToAddress(message, 1); + } else { + (, beneficiary) = abi.decode(message, (uint8, address)); + } + beneficiaries[staker] = beneficiary; + } + + function setWithdraw( + address staker, + bytes calldata message, + bytes memory origin + ) internal { + bytes memory withdraw; + if (chainID == BITCOIN) { + withdraw = bytesToBech32Bytes(message, 1); + } else { + withdraw = origin; + } + withdrawAddresses[staker] = withdraw; + } + function queryRewards(address staker) public view returns (uint256) { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; return rewardAmount; } + + function claimRewards(address staker) public { + require( + beneficiaries[staker] == msg.sender, + "Not authorized to claim rewards" + ); + uint256 rewardAmount = queryRewards(staker); + require(rewardAmount > 0, "No rewards to claim"); + updateRewards(staker); + } } diff --git a/omnichain/staking/hardhat.config.ts b/omnichain/staking/hardhat.config.ts index caca0f35..a60f3695 100644 --- a/omnichain/staking/hardhat.config.ts +++ b/omnichain/staking/hardhat.config.ts @@ -1,11 +1,11 @@ import "./tasks/stake"; import "./tasks/deploy"; -import "./tasks/rewards"; import "./tasks/claim"; import "./tasks/unstake"; import "./tasks/beneficiary"; import "./tasks/withdraw"; import "./tasks/unstake"; +import "./tasks/address"; import "@nomicfoundation/hardhat-toolbox"; import "@zetachain/toolkit/tasks"; diff --git a/omnichain/staking/package.json b/omnichain/staking/package.json index c6542bd3..0e20b9ba 100644 --- a/omnichain/staking/package.json +++ b/omnichain/staking/package.json @@ -26,7 +26,7 @@ "@types/node": ">=12.0.0", "@typescript-eslint/eslint-plugin": "^5.59.9", "@typescript-eslint/parser": "^5.59.9", - "@zetachain/toolkit": "^2.1.0", + "@zetachain/toolkit": "^2.2.2", "axios": "^1.3.6", "chai": "^4.2.0", "dotenv": "^16.0.3", @@ -48,4 +48,4 @@ "typechain": "^8.1.0", "typescript": ">=4.5.0" } -} \ No newline at end of file +} diff --git a/omnichain/staking/tasks/address.ts b/omnichain/staking/tasks/address.ts new file mode 100644 index 00000000..eae32c8c --- /dev/null +++ b/omnichain/staking/tasks/address.ts @@ -0,0 +1,18 @@ +import { task } from "hardhat/config"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { utils } from "ethers"; + +const main = async (args: any, hre: HardhatRuntimeEnvironment) => { + const dataTypes = ["bytes"]; + const values = [utils.toUtf8Bytes(args.address)]; + + const encodedData = utils.solidityPack(dataTypes, values); + console.log(`Encoded: ${encodedData}`); + console.log(`context.origin: ${encodedData.slice(0, 42)}`); +}; + +task( + "address", + "Encode a Bitcoin bech32 address to hex", + main +).addPositionalParam("address"); diff --git a/omnichain/staking/tasks/beneficiary.ts b/omnichain/staking/tasks/beneficiary.ts index de652c64..27d6f711 100644 --- a/omnichain/staking/tasks/beneficiary.ts +++ b/omnichain/staking/tasks/beneficiary.ts @@ -24,7 +24,11 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await trackCCTX(tx.hash); }; -task("beneficiary", "", main) - .addParam("contract", "The address of the withdraw contract on ZetaChain") +task( + "set-beneficiary", + "Set the address on ZetaChain which will be allowed to claim staking rewards", + main +) + .addParam("contract", "The address of the contract on ZetaChain") .addParam("amount", "Amount of tokens to send") .addPositionalParam("beneficiary", "The address of the beneficiary"); diff --git a/omnichain/staking/tasks/rewards.ts b/omnichain/staking/tasks/rewards.ts deleted file mode 100644 index 941405c4..00000000 --- a/omnichain/staking/tasks/rewards.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { task } from "hardhat/config"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { convertToHexAddress } from "../lib/convertToHexAddress"; -const { decode } = require("bech32"); -const { arrayify } = require("@ethersproject/bytes"); - -function bech32ToHex(bech32Address: string) { - const { words } = decode(bech32Address); - - // Convert 5-bit words to bytes - let bytes = []; - for (let i = 0; i < words.length; i++) { - let word = words[i]; - for (let j = 4; j >= 0; j--) { - bytes.push((word >> j) & 1); - } - } - - return arrayify(bytes); -} -const main = async (args: any, hre: HardhatRuntimeEnvironment) => { - const [signer] = await hre.ethers.getSigners(); - console.log(`🔑 Using account: ${signer.address}\n`); - - const staker = convertToHexAddress(args.staker); - - const factory = await hre.ethers.getContractFactory("Staking"); - const contract = factory.attach(args.contract); - - console.log(await contract.queryRewards(bech32ToHex(args.staker))); -}; - -task("rewards", "Query staking rewards", main) - .addParam("contract", "The address of the contract on ZetaChain") - .addParam("staker", "Staker address"); diff --git a/omnichain/staking/tasks/stake.ts b/omnichain/staking/tasks/stake.ts index 7bcec388..4f58c4ed 100644 --- a/omnichain/staking/tasks/stake.ts +++ b/omnichain/staking/tasks/stake.ts @@ -20,6 +20,6 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await trackCCTX(tx.hash); }; -task("stake", "Interact with the contract", main) - .addParam("contract", "The address of the withdraw contract on ZetaChain") +task("stake", "Deposit tokens to ZetaChain and stake them", main) + .addParam("contract", "The address of the contract on ZetaChain") .addParam("amount", "Amount of tokens to send"); diff --git a/omnichain/staking/tasks/withdraw.ts b/omnichain/staking/tasks/withdraw.ts index 42e56fe5..b0c02d1c 100644 --- a/omnichain/staking/tasks/withdraw.ts +++ b/omnichain/staking/tasks/withdraw.ts @@ -20,6 +20,10 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await trackCCTX(tx.hash); }; -task("withdraw", "", main) - .addParam("contract", "The address of the withdraw contract on ZetaChain") +task( + "set-withdraw-address", + "Set the address on a connected chain to which unstaked tokens will be withdrawn", + main +) + .addParam("contract", "The address of the contract on ZetaChain") .addParam("amount", "Amount of tokens to send"); diff --git a/omnichain/staking/yarn.lock b/omnichain/staking/yarn.lock index efa836f7..624ebbbe 100644 --- a/omnichain/staking/yarn.lock +++ b/omnichain/staking/yarn.lock @@ -96,7 +96,22 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.4.1.tgz#6ac28fafc9ef6f5a7a37e30356a2eb31fa05d39b" + integrity sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg== + dependencies: + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -111,7 +126,20 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": +"@ethersproject/abstract-provider@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz#e404309a29f771bd4d28dbafadcaa184668c2a6e" + integrity sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/networks" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/web" "^5.4.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.4.0", "@ethersproject/abstract-provider@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== @@ -124,7 +152,18 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": +"@ethersproject/abstract-signer@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz#e4e9abcf4dd4f1ba0db7dff9746a5f78f355ea81" + integrity sha512-SkkFL5HVq1k4/25dM+NWP9MILgohJCgGv5xT5AcRruGz4ILpfHeBtO/y6j+Z3UN/PAjDeb4P7E51Yh8wcGNLGA== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== @@ -135,7 +174,18 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.4.0.tgz#ba2d00a0f8c4c0854933b963b9a3a9f6eb4a37a3" + integrity sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -146,14 +196,29 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": +"@ethersproject/base64@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.4.0.tgz#7252bf65295954c9048c7ca5f43e5c86441b2a9a" + integrity sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ== + dependencies: + "@ethersproject/bytes" "^5.4.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.4.0", "@ethersproject/base64@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== dependencies: "@ethersproject/bytes" "^5.7.0" -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": +"@ethersproject/basex@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.4.0.tgz#0a2da0f4e76c504a94f2b21d3161ed9438c7f8a6" + integrity sha512-J07+QCVJ7np2bcpxydFVf/CuYo9mZ7T73Pe7KQY4c1lRlrixMeblauMxHXD0MPwFmUHZIILDNViVkykFBZylbg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.4.0", "@ethersproject/basex@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== @@ -161,7 +226,16 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": +"@ethersproject/bignumber@5.4.2": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.4.2.tgz#44232e015ae4ce82ac034de549eb3583c71283d8" + integrity sha512-oIBDhsKy5bs7j36JlaTzFgNPaZjiNDOXsdSgSpXRucUl+UA6L/1YLlFeI3cPAoodcenzF4nxNPV13pcy7XbWjA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + bn.js "^4.11.9" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.4.0", "@ethersproject/bignumber@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== @@ -170,20 +244,50 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e" + integrity sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.4.0", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": +"@ethersproject/constants@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.4.0.tgz#ee0bdcb30bf1b532d2353c977bf2ef1ee117958a" + integrity sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.4.0", "@ethersproject/constants@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== dependencies: "@ethersproject/bignumber" "^5.7.0" +"@ethersproject/contracts@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.4.1.tgz#3eb4f35b7fe60a962a75804ada2746494df3e470" + integrity sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w== + dependencies: + "@ethersproject/abi" "^5.4.0" + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/contracts@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" @@ -200,7 +304,21 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": +"@ethersproject/hash@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.4.0.tgz#d18a8e927e828e22860a011f39e429d388344ae0" + integrity sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.4.0", "@ethersproject/hash@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== @@ -215,7 +333,25 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": +"@ethersproject/hdnode@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.4.0.tgz#4bc9999b9a12eb5ce80c5faa83114a57e4107cac" + integrity sha512-pKxdS0KAaeVGfZPp1KOiDLB0jba11tG6OP1u11QnYfb7pXn6IZx0xceqWRr6ygke8+Kw74IpOoSi7/DwANhy8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/basex" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/pbkdf2" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/wordlists" "^5.4.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.4.0", "@ethersproject/hdnode@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== @@ -233,7 +369,26 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": +"@ethersproject/json-wallets@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.4.0.tgz#2583341cfe313fc9856642e8ace3080154145e95" + integrity sha512-igWcu3fx4aiczrzEHwG1xJZo9l1cFfQOWzTqwRw/xcvxTk58q4f9M7cjh51EKphMHvrJtcezJ1gf1q1AUOfEQQ== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hdnode" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/pbkdf2" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.4.0", "@ethersproject/json-wallets@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== @@ -252,7 +407,15 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": +"@ethersproject/keccak256@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.4.0.tgz#7143b8eea4976080241d2bd92e3b1f1bf7025318" + integrity sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A== + dependencies: + "@ethersproject/bytes" "^5.4.0" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.4.0", "@ethersproject/keccak256@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== @@ -260,19 +423,39 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": +"@ethersproject/logger@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.4.1.tgz#503bd33683538b923c578c07d1c2c0dd18672054" + integrity sha512-DZ+bRinnYLPw1yAC64oRl0QyVZj43QeHIhVKfD/+YwSz4wsv1pfwb5SOFjz+r710YEWzU6LrhuSjpSO+6PeE4A== + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.4.0", "@ethersproject/logger@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": +"@ethersproject/networks@5.4.2": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.2.tgz#2247d977626e97e2c3b8ee73cd2457babde0ce35" + integrity sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.4.0", "@ethersproject/networks@^5.7.0": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": +"@ethersproject/pbkdf2@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.4.0.tgz#ed88782a67fda1594c22d60d0ca911a9d669641c" + integrity sha512-x94aIv6tiA04g6BnazZSLoRXqyusawRyZWlUhKip2jvoLpzJuLb//KtMM6PEovE47pMbW+Qe1uw+68ameJjB7g== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.4.0", "@ethersproject/pbkdf2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== @@ -280,13 +463,45 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": +"@ethersproject/properties@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.4.1.tgz#9f051f976ce790142c6261ccb7b826eaae1f2f36" + integrity sha512-cyCGlF8wWlIZyizsj2PpbJ9I7rIlUAfnHYwy/T90pdkSn/NFTa5YWZx2wTJBe9V7dD65dcrrEMisCRUJiq6n3w== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.4.0", "@ethersproject/properties@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/providers@5.4.5": + version "5.4.5" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928" + integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/basex" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/networks" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/web" "^5.4.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.4.7", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" @@ -313,7 +528,15 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": +"@ethersproject/random@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16" + integrity sha512-pnpWNQlf0VAZDEOVp1rsYQosmv2o0ITS/PecNw+mS2/btF8eYdspkN0vIXrCMtkX09EAh9bdk8GoXmFXM1eAKw== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.4.0", "@ethersproject/random@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== @@ -321,7 +544,15 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.4.0.tgz#de61afda5ff979454e76d3b3310a6c32ad060931" + integrity sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.4.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -329,7 +560,16 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": +"@ethersproject/sha2@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.4.0.tgz#c9a8db1037014cbc4e9482bd662f86c090440371" + integrity sha512-siheo36r1WD7Cy+bDdE1BJ8y0bDtqXCOxRMzPa4bV1TGt/eTUUt03BHoJNB6reWJD8A30E/pdJ8WFkq+/uz4Gg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + hash.js "1.1.7" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.4.0", "@ethersproject/sha2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== @@ -338,7 +578,19 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": +"@ethersproject/signing-key@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.4.0.tgz#2f05120984e81cf89a3d5f6dec5c68ee0894fbec" + integrity sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.4.0", "@ethersproject/signing-key@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== @@ -350,6 +602,17 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/solidity@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.4.0.tgz#1305e058ea02dc4891df18b33232b11a14ece9ec" + integrity sha512-XFQTZ7wFSHOhHcV1DpcWj7VXECEiSrBuv7JErJvB9Uo+KfCdc3QtUZV+Vjh/AAaYgezUEKbCtE6Khjm44seevQ== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/solidity@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" @@ -362,7 +625,16 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": +"@ethersproject/strings@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.4.0.tgz#fb12270132dd84b02906a8d895ae7e7fa3d07d9a" + integrity sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.4.0", "@ethersproject/strings@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== @@ -371,7 +643,22 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.4.0.tgz#a159d035179334bd92f340ce0f77e83e9e1522e0" + integrity sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ== + dependencies: + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.4.0", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -386,6 +673,15 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" +"@ethersproject/units@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.4.0.tgz#d57477a4498b14b88b10396062c8cbbaf20c79fe" + integrity sha512-Z88krX40KCp+JqPCP5oPv5p750g+uU6gopDYRTBGcDvOASh6qhiEYCRatuM/suC4S2XW9Zz90QI35MfSrTIaFg== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/units@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" @@ -395,6 +691,27 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/wallet@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.4.0.tgz#fa5b59830b42e9be56eadd45a16a2e0933ad9353" + integrity sha512-wU29majLjM6AjCjpat21mPPviG+EpK7wY1+jzKD0fg3ui5fgedf2zEu1RDgpfIMsfn8fJHJuzM4zXZ2+hSHaSQ== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/hdnode" "^5.4.0" + "@ethersproject/json-wallets" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/wordlists" "^5.4.0" + "@ethersproject/wallet@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" @@ -416,7 +733,18 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": +"@ethersproject/web@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.4.0.tgz#49fac173b96992334ed36a175538ba07a7413d1f" + integrity sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og== + dependencies: + "@ethersproject/base64" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.4.0", "@ethersproject/web@^5.7.0": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== @@ -427,7 +755,18 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": +"@ethersproject/wordlists@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.4.0.tgz#f34205ec3bbc9e2c49cadaee774cf0b07e7573d7" + integrity sha512-FemEkf6a+EBKEPxlzeVgUaVSodU7G0Na89jqKjmWMlDB0tomoU8RlEMgUvXyqtrg8N4cwpLh8nyRnm1Nay1isA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.4.0", "@ethersproject/wordlists@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== @@ -477,6 +816,23 @@ "@inquirer/type" "^1.1.2" chalk "^4.1.2" +"@inquirer/core@^1.1.3": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-1.3.0.tgz#469427e51daa519f2b1332745a2222629c03c701" + integrity sha512-W7EA48gIMahFLiGW/zF+rgoineqTDK5IQizsOmwvbFfYgiQ8Asetut94THBmB3KnW0nrZL5UPHUK6QzcjEzaCw== + dependencies: + "@inquirer/type" "^1.0.5" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + cli-spinners "^2.8.0" + cli-width "^4.0.0" + figures "^3.2.0" + mute-stream "^1.0.0" + run-async "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wrap-ansi "^6.0.1" + "@inquirer/core@^2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-2.3.1.tgz#b7a1563ef3830a20485f551257779657e843e53f" @@ -579,6 +935,17 @@ "@inquirer/type" "^1.1.2" chalk "^4.1.2" +"@inquirer/select@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-1.1.3.tgz#7974d1beff6b87c981a9e25e1ef4cb237f03c1b2" + integrity sha512-R3ypgFRq7QKz9l8uqlEPRkK4dhkkVFy6gzuMPWzkHFTiWBDIjHTwD8b41eQwQW8SRAVls6VyTdtA/Sf0te/bVw== + dependencies: + "@inquirer/core" "^1.1.3" + "@inquirer/type" "^1.0.3" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + figures "^3.2.0" + "@inquirer/select@^1.2.4": version "1.2.8" resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-1.2.8.tgz#ce1b529a8d208afcb22819beb6d22f0ce2121742" @@ -590,6 +957,11 @@ chalk "^4.1.2" figures "^3.2.0" +"@inquirer/type@^1.0.3", "@inquirer/type@^1.0.5": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.1.4.tgz#8ef3d3d638a59253fbbe4b0297035cddc227eaed" + integrity sha512-a6+RCiXBQEbiA73RT1pBfwiH2I+MPcoZoGKuuUYFkws+6ud7nb5kUQGHFGUSBim25IyVMT/mqbWIrkxetcIb/w== + "@inquirer/type@^1.1.1", "@inquirer/type@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.1.2.tgz#5c576904707cec5f6ee0097b0a3f355e617ae7c8" @@ -1359,7 +1731,7 @@ "@uniswap/lib" "1.1.1" "@uniswap/v2-core" "1.0.0" -"@zetachain/faucet-cli@^2.0.2-athens3.1": +"@zetachain/faucet-cli@2.0.2-athens3.1": version "2.0.2-athens3.1" resolved "https://registry.yarnpkg.com/@zetachain/faucet-cli/-/faucet-cli-2.0.2-athens3.1.tgz#e4d2224097b7620f46ec9a985b45625d350340e3" integrity sha512-BDtpQF7XpYc49BQsPRfi6kcRBKjs2UESPzzK7jTcjRRxsFjMQ9tLnY3cx65U/WjFaS2O0APE4vo96j6lQry7PQ== @@ -1383,16 +1755,17 @@ resolved "https://registry.yarnpkg.com/@zetachain/protocol-contracts/-/protocol-contracts-2.1.0.tgz#775b4eee7c85d115232dece121cbfc798fde6b63" integrity sha512-xmG6p8DizIk0h7Tr8Lt6UMG0ejrfRrPx0qH9ze3enwblo7W+eGP12oQ7djanYu50B0pNhh9z7xy/IYxKa9wD0Q== -"@zetachain/toolkit@^2.1.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@zetachain/toolkit/-/toolkit-2.1.1.tgz#f351c91c103915f69d6cb984a3d707b16c2f39b7" - integrity sha512-xZQjK+zlV4h06tBfu+wmkYOY7vT0PfjEJkh5IHnbNZuZq+oqQVUtdOHp7bSgx3RU+OiwjrATqfRtUIA9stw4Kg== +"@zetachain/toolkit@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@zetachain/toolkit/-/toolkit-2.2.2.tgz#17f5957bdff6e3e508a8bdd06e4d7228de8bb888" + integrity sha512-C42kje55PEIIX3cQ9m36w4w+mL5CyFx0P/F3PdXsrOtub3hNLi0Es1j0hFqlAtUtBnyfLCE772a97j0ubyWFjQ== dependencies: "@inquirer/prompts" "^2.1.1" + "@inquirer/select" "1.1.3" "@nomiclabs/hardhat-ethers" "^2.2.3" "@openzeppelin/contracts" "^4.9.2" "@uniswap/v2-periphery" "^1.1.0-beta.0" - "@zetachain/faucet-cli" "^2.0.2-athens3.1" + "@zetachain/faucet-cli" "2.0.2-athens3.1" "@zetachain/networks" "^2.3.0-athens3" "@zetachain/protocol-contracts" "^2.1.0" axios "^1.4.0" @@ -1400,13 +1773,15 @@ bip39 "^3.1.0" bitcoinjs-lib "^6.1.3" cli-color "^2.0.3" + dotenv "16.0.3" ecpair "^2.1.0" - ethers "^5.4.7" + envfile "^6.18.0" + ethers "5.4.7" form-data "^4.0.0" + handlebars "4.7.7" hardhat "^2.15.0" moment "^2.29.4" ora "5.4.1" - readline "^1.3.0" spinnies "^0.5.1" tiny-secp256k1 "^2.2.3" ws "^8.13.0" @@ -2520,6 +2895,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dotenv@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + dotenv@^16.0.3, dotenv@^16.1.4: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" @@ -3057,6 +3437,42 @@ ethereumjs-util@^7.1.4: ethereum-cryptography "^0.1.3" rlp "^2.2.4" +ethers@5.4.7: + version "5.4.7" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.4.7.tgz#0fd491a5da7c9793de2d6058d76b41b1e7efba8f" + integrity sha512-iZc5p2nqfWK1sj8RabwsPM28cr37Bpq7ehTQ5rWExBr2Y09Sn1lDKZOED26n+TsZMye7Y6mIgQ/1cwpSD8XZew== + dependencies: + "@ethersproject/abi" "5.4.1" + "@ethersproject/abstract-provider" "5.4.1" + "@ethersproject/abstract-signer" "5.4.1" + "@ethersproject/address" "5.4.0" + "@ethersproject/base64" "5.4.0" + "@ethersproject/basex" "5.4.0" + "@ethersproject/bignumber" "5.4.2" + "@ethersproject/bytes" "5.4.0" + "@ethersproject/constants" "5.4.0" + "@ethersproject/contracts" "5.4.1" + "@ethersproject/hash" "5.4.0" + "@ethersproject/hdnode" "5.4.0" + "@ethersproject/json-wallets" "5.4.0" + "@ethersproject/keccak256" "5.4.0" + "@ethersproject/logger" "5.4.1" + "@ethersproject/networks" "5.4.2" + "@ethersproject/pbkdf2" "5.4.0" + "@ethersproject/properties" "5.4.1" + "@ethersproject/providers" "5.4.5" + "@ethersproject/random" "5.4.0" + "@ethersproject/rlp" "5.4.0" + "@ethersproject/sha2" "5.4.0" + "@ethersproject/signing-key" "5.4.0" + "@ethersproject/solidity" "5.4.0" + "@ethersproject/strings" "5.4.0" + "@ethersproject/transactions" "5.4.0" + "@ethersproject/units" "5.4.0" + "@ethersproject/wallet" "5.4.0" + "@ethersproject/web" "5.4.0" + "@ethersproject/wordlists" "5.4.0" + ethers@^4.0.40: version "4.0.49" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" @@ -3662,6 +4078,18 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + handlebars@^4.0.1: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" @@ -4774,7 +5202,7 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -neo-async@^2.6.2: +neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -5274,11 +5702,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -readline@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" - integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" From c4ccaf94d89db7a1bf539b912bca0c7662ab314a Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Wed, 20 Sep 2023 19:21:54 +0400 Subject: [PATCH 18/27] refactor --- omnichain/staking/contracts/Staking.sol | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 5c63fb10..1fe9bde8 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -31,6 +31,14 @@ contract Staking is ERC20, zContract { chainID = chainID_; } + modifier onlySystem() { + require( + msg.sender == address(systemContract), + "Only system contract can call this function" + ); + _; + } + function bytesToBech32Bytes( bytes calldata data, uint256 offset @@ -48,7 +56,7 @@ contract Staking is ERC20, zContract { address zrc20, uint256 amount, bytes calldata message - ) external override { + ) external override onlySystem { if (msg.sender != address(systemContract)) { revert SenderNotSystemContract(); } @@ -85,9 +93,7 @@ contract Staking is ERC20, zContract { } function updateRewards(address staker) internal { - uint256 timeDifference = block.timestamp - lastStakeTime[staker]; - uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; - require(rewardAmount >= timeDifference, "Overflow detected"); + uint256 rewardAmount = queryRewards(staker); _mint(beneficiaries[staker], rewardAmount); lastStakeTime[staker] = block.timestamp; From 32658e19408b9313fc864ed30743c4fdfc882324 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Thu, 21 Sep 2023 10:24:50 +0400 Subject: [PATCH 19/27] remove amount from tasks --- omnichain/staking/tasks/beneficiary.ts | 3 +-- omnichain/staking/tasks/unstake.ts | 9 +++++---- omnichain/staking/tasks/withdraw.ts | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/omnichain/staking/tasks/beneficiary.ts b/omnichain/staking/tasks/beneficiary.ts index 27d6f711..468448ed 100644 --- a/omnichain/staking/tasks/beneficiary.ts +++ b/omnichain/staking/tasks/beneficiary.ts @@ -14,7 +14,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { ["3", args.beneficiary] ); const to = getAddress("tss", hre.network.name); - const value = parseEther(args.amount); + const value = parseEther("0"); const tx = await signer.sendTransaction({ data, to, value }); console.log(` @@ -30,5 +30,4 @@ task( main ) .addParam("contract", "The address of the contract on ZetaChain") - .addParam("amount", "Amount of tokens to send") .addPositionalParam("beneficiary", "The address of the beneficiary"); diff --git a/omnichain/staking/tasks/unstake.ts b/omnichain/staking/tasks/unstake.ts index 8f7ba76b..5ebc2d47 100644 --- a/omnichain/staking/tasks/unstake.ts +++ b/omnichain/staking/tasks/unstake.ts @@ -10,7 +10,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const data = prepareData(args.contract, ["uint8"], ["2"]); const to = getAddress("tss", hre.network.name); - const value = parseEther(args.amount); + const value = parseEther("0"); const tx = await signer.sendTransaction({ data, to, value }); console.log(` @@ -20,6 +20,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { await trackCCTX(tx.hash); }; -task("unstake", "Unstake tokens", main) - .addParam("contract", "The address of the contract on ZetaChain") - .addParam("amount", "Amount of tokens to unstake"); +task("unstake", "Unstake tokens", main).addParam( + "contract", + "The address of the contract on ZetaChain" +); diff --git a/omnichain/staking/tasks/withdraw.ts b/omnichain/staking/tasks/withdraw.ts index b0c02d1c..7641b509 100644 --- a/omnichain/staking/tasks/withdraw.ts +++ b/omnichain/staking/tasks/withdraw.ts @@ -10,7 +10,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { const data = prepareData(args.contract, ["uint8"], ["4"]); const to = getAddress("tss", hre.network.name); - const value = parseEther(args.amount); + const value = parseEther("0"); const tx = await signer.sendTransaction({ data, to, value }); console.log(` @@ -24,6 +24,4 @@ task( "set-withdraw-address", "Set the address on a connected chain to which unstaked tokens will be withdrawn", main -) - .addParam("contract", "The address of the contract on ZetaChain") - .addParam("amount", "Amount of tokens to send"); +).addParam("contract", "The address of the contract on ZetaChain"); From 13e3680a185f3663a836659ceb2d43ab6fb7b768 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 09:52:54 +0400 Subject: [PATCH 20/27] spacing --- omnichain/staking/contracts/Staking.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 1fe9bde8..22501de8 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -12,9 +12,10 @@ contract Staking is ERC20, zContract { error UnknownAction(); SystemContract public immutable systemContract; + uint256 constant BITCOIN = 18332; + uint256 public immutable chainID; uint256 public rewardRate = 1; - uint256 constant BITCOIN = 18332; mapping(address => uint256) public stakes; mapping(address => bytes) public withdrawAddresses; From 8d4970fef3ce922906af4dabcbdf994296b765ca Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 09:58:52 +0400 Subject: [PATCH 21/27] naming --- omnichain/staking/contracts/Staking.sol | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 22501de8..0e71a901 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -17,9 +17,9 @@ contract Staking is ERC20, zContract { uint256 public immutable chainID; uint256 public rewardRate = 1; - mapping(address => uint256) public stakes; - mapping(address => bytes) public withdrawAddresses; - mapping(address => address) public beneficiaries; + mapping(address => uint256) public stake; + mapping(address => bytes) public withdraw; + mapping(address => address) public beneficiary; mapping(address => uint256) public lastStakeTime; constructor( @@ -86,8 +86,8 @@ contract Staking is ERC20, zContract { } function stakeZRC(address staker, uint256 amount) internal { - stakes[staker] += amount; - require(stakes[staker] >= amount, "Overflow detected"); + stake[staker] += amount; + require(stake[staker] >= amount, "Overflow detected"); lastStakeTime[staker] = block.timestamp; updateRewards(staker); @@ -96,12 +96,12 @@ contract Staking is ERC20, zContract { function updateRewards(address staker) internal { uint256 rewardAmount = queryRewards(staker); - _mint(beneficiaries[staker], rewardAmount); + _mint(beneficiary[staker], rewardAmount); lastStakeTime[staker] = block.timestamp; } function unstakeZRC(address staker) internal { - uint256 amount = stakes[staker]; + uint256 amount = stake[staker]; updateRewards(staker); @@ -110,25 +110,25 @@ contract Staking is ERC20, zContract { require(amount >= gasFee, "Amount should be greater than the gas fee"); - bytes memory recipient = withdrawAddresses[staker]; + bytes memory recipient = withdraw[staker]; IZRC20(zrc20).approve(zrc20, gasFee); IZRC20(zrc20).withdraw(recipient, amount - gasFee); - stakes[staker] = 0; - require(stakes[staker] <= amount, "Underflow detected"); + stake[staker] = 0; + require(stake[staker] <= amount, "Underflow detected"); lastStakeTime[staker] = block.timestamp; } function setBeneficiary(address staker, bytes calldata message) internal { - address beneficiary; + address beneficiaryAddress; if (chainID == BITCOIN) { - beneficiary = BytesHelperLib.bytesToAddress(message, 1); + beneficiaryAddress = BytesHelperLib.bytesToAddress(message, 1); } else { - (, beneficiary) = abi.decode(message, (uint8, address)); + (, beneficiaryAddress) = abi.decode(message, (uint8, address)); } - beneficiaries[staker] = beneficiary; + beneficiary[staker] = beneficiaryAddress; } function setWithdraw( @@ -136,24 +136,24 @@ contract Staking is ERC20, zContract { bytes calldata message, bytes memory origin ) internal { - bytes memory withdraw; + bytes memory withdrawAddress; if (chainID == BITCOIN) { - withdraw = bytesToBech32Bytes(message, 1); + withdrawAddress = bytesToBech32Bytes(message, 1); } else { - withdraw = origin; + withdrawAddress = origin; } - withdrawAddresses[staker] = withdraw; + withdraw[staker] = withdrawAddress; } function queryRewards(address staker) public view returns (uint256) { uint256 timeDifference = block.timestamp - lastStakeTime[staker]; - uint256 rewardAmount = timeDifference * stakes[staker] * rewardRate; + uint256 rewardAmount = timeDifference * stake[staker] * rewardRate; return rewardAmount; } function claimRewards(address staker) public { require( - beneficiaries[staker] == msg.sender, + beneficiary[staker] == msg.sender, "Not authorized to claim rewards" ); uint256 rewardAmount = queryRewards(staker); From 967401c19bcaec2cdb789c65e4b380dace6e2520 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 09:59:25 +0400 Subject: [PATCH 22/27] internal --- omnichain/staking/contracts/Staking.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 0e71a901..b214ce78 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -43,7 +43,7 @@ contract Staking is ERC20, zContract { function bytesToBech32Bytes( bytes calldata data, uint256 offset - ) public pure returns (bytes memory) { + ) internal pure returns (bytes memory) { bytes memory bech32Bytes = new bytes(42); for (uint i = 0; i < 42; i++) { bech32Bytes[i] = data[i + offset]; From d32bb85a27f08d4c75f2034c5d4f38bdca310648 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 10:01:54 +0400 Subject: [PATCH 23/27] errors with params --- omnichain/staking/contracts/Staking.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index b214ce78..966731d2 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -8,8 +8,8 @@ import "@zetachain/toolkit/contracts/BytesHelperLib.sol"; contract Staking is ERC20, zContract { error SenderNotSystemContract(); - error WrongChain(); - error UnknownAction(); + error WrongChain(uint256 chainID); + error UnknownAction(uint8 action); SystemContract public immutable systemContract; uint256 constant BITCOIN = 18332; @@ -63,7 +63,7 @@ contract Staking is ERC20, zContract { } if (chainID != context.chainID) { - revert WrongChain(); + revert WrongChain(context.chainID); } address staker = BytesHelperLib.bytesToAddress(context.origin, 0); @@ -81,7 +81,7 @@ contract Staking is ERC20, zContract { } else if (action == 4) { setWithdraw(staker, message, context.origin); } else { - revert UnknownAction(); + revert UnknownAction(action); } } From 3a466b3b1b741ae7ecee37a5ad7e735f225f9d26 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 10:11:33 +0400 Subject: [PATCH 24/27] custom errors --- omnichain/staking/contracts/Staking.sol | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 966731d2..2dbfaed6 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -10,6 +10,11 @@ contract Staking is ERC20, zContract { error SenderNotSystemContract(); error WrongChain(uint256 chainID); error UnknownAction(uint8 action); + error Overflow(); + error Underflow(); + error WrongAmount(); + error NotAuthorized(); + error NoRewardsToClaim(); SystemContract public immutable systemContract; uint256 constant BITCOIN = 18332; @@ -58,10 +63,6 @@ contract Staking is ERC20, zContract { uint256 amount, bytes calldata message ) external override onlySystem { - if (msg.sender != address(systemContract)) { - revert SenderNotSystemContract(); - } - if (chainID != context.chainID) { revert WrongChain(context.chainID); } @@ -87,7 +88,7 @@ contract Staking is ERC20, zContract { function stakeZRC(address staker, uint256 amount) internal { stake[staker] += amount; - require(stake[staker] >= amount, "Overflow detected"); + if (stake[staker] < amount) revert Overflow(); lastStakeTime[staker] = block.timestamp; updateRewards(staker); @@ -108,7 +109,7 @@ contract Staking is ERC20, zContract { address zrc20 = systemContract.gasCoinZRC20ByChainId(chainID); (, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee(); - require(amount >= gasFee, "Amount should be greater than the gas fee"); + if (amount < gasFee) revert WrongAmount(); bytes memory recipient = withdraw[staker]; @@ -116,7 +117,7 @@ contract Staking is ERC20, zContract { IZRC20(zrc20).withdraw(recipient, amount - gasFee); stake[staker] = 0; - require(stake[staker] <= amount, "Underflow detected"); + if (stake[staker] > amount) revert Underflow(); lastStakeTime[staker] = block.timestamp; } @@ -152,12 +153,9 @@ contract Staking is ERC20, zContract { } function claimRewards(address staker) public { - require( - beneficiary[staker] == msg.sender, - "Not authorized to claim rewards" - ); + if (beneficiary[staker] != msg.sender) revert NotAuthorized(); uint256 rewardAmount = queryRewards(staker); - require(rewardAmount > 0, "No rewards to claim"); + if (rewardAmount <= 0) revert NoRewardsToClaim(); updateRewards(staker); } } From 25fdfa70f1e9364b5b08a831fd862bdf7dcf0fa5 Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 10:14:17 +0400 Subject: [PATCH 25/27] withdraw --- omnichain/staking/contracts/Staking.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 2dbfaed6..2f1fb322 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -113,10 +113,11 @@ contract Staking is ERC20, zContract { bytes memory recipient = withdraw[staker]; + stake[staker] = 0; + IZRC20(zrc20).approve(zrc20, gasFee); IZRC20(zrc20).withdraw(recipient, amount - gasFee); - stake[staker] = 0; if (stake[staker] > amount) revert Underflow(); lastStakeTime[staker] = block.timestamp; From bbb21498f2bd1e06d143b213c7f7a823f042355a Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 10:14:39 +0400 Subject: [PATCH 26/27] external --- omnichain/staking/contracts/Staking.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 2f1fb322..24196ec3 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -153,7 +153,7 @@ contract Staking is ERC20, zContract { return rewardAmount; } - function claimRewards(address staker) public { + function claimRewards(address staker) external { if (beneficiary[staker] != msg.sender) revert NotAuthorized(); uint256 rewardAmount = queryRewards(staker); if (rewardAmount <= 0) revert NoRewardsToClaim(); From 3f8585e258f7e41fa9b10bfef5a1ab9565004fde Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 22 Sep 2023 10:38:34 +0400 Subject: [PATCH 27/27] variable grouping --- omnichain/staking/contracts/Staking.sol | 2 +- omnichain/staking/tasks/withdraw.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/omnichain/staking/contracts/Staking.sol b/omnichain/staking/contracts/Staking.sol index 24196ec3..c68427bb 100644 --- a/omnichain/staking/contracts/Staking.sol +++ b/omnichain/staking/contracts/Staking.sol @@ -17,9 +17,9 @@ contract Staking is ERC20, zContract { error NoRewardsToClaim(); SystemContract public immutable systemContract; + uint256 public immutable chainID; uint256 constant BITCOIN = 18332; - uint256 public immutable chainID; uint256 public rewardRate = 1; mapping(address => uint256) public stake; diff --git a/omnichain/staking/tasks/withdraw.ts b/omnichain/staking/tasks/withdraw.ts index 7641b509..c7ef8d90 100644 --- a/omnichain/staking/tasks/withdraw.ts +++ b/omnichain/staking/tasks/withdraw.ts @@ -21,7 +21,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { }; task( - "set-withdraw-address", + "set-withdraw", "Set the address on a connected chain to which unstaked tokens will be withdrawn", main ).addParam("contract", "The address of the contract on ZetaChain");