From 7af6edfaedd848676bb290ece09206a5865f42bc Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Sun, 28 Mar 2021 23:55:10 +0300 Subject: [PATCH 01/25] Implement StakeWise token --- .../interfaces/IERC20PermitUpgradeable.sol | 52 -------- contracts/libraries/ECDSA.sol | 87 ------------ contracts/tokens/EIP712Upgradeable.sol | 125 ------------------ contracts/tokens/ERC20PermitUpgradeable.sol | 18 +-- contracts/tokens/ERC20Upgradeable.sol | 20 +-- contracts/tokens/StakeWiseToken.sol | 59 +++++++++ deployments/tokens.js | 10 +- 7 files changed, 89 insertions(+), 282 deletions(-) delete mode 100644 contracts/interfaces/IERC20PermitUpgradeable.sol delete mode 100644 contracts/libraries/ECDSA.sol delete mode 100644 contracts/tokens/EIP712Upgradeable.sol create mode 100644 contracts/tokens/StakeWiseToken.sol diff --git a/contracts/interfaces/IERC20PermitUpgradeable.sol b/contracts/interfaces/IERC20PermitUpgradeable.sol deleted file mode 100644 index 5b382f3b..00000000 --- a/contracts/interfaces/IERC20PermitUpgradeable.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/a4e82ad71d24cf5ab1778f1947441f24903da86a/contracts/drafts/IERC20PermitUpgradeable.sol - -pragma solidity 0.7.5; - -/** - * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in - * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - * - * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by - * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't - * need to send a transaction, and thus is not required to hold Ether at all. - */ -interface IERC20PermitUpgradeable { - /** - * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, - * given `owner`'s signed approval. - * - * IMPORTANT: The same issues {IERC20-approve} has related to transaction - * ordering also apply here. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `deadline` must be a timestamp in the future. - * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` - * over the EIP712-formatted function arguments. - * - the signature must use ``owner``'s current nonce (see {nonces}). - * - * For more information on the signature format, see the - * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP - * section]. - */ - function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) external view returns (uint256); - - /** - * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view returns (bytes32); -} diff --git a/contracts/libraries/ECDSA.sol b/contracts/libraries/ECDSA.sol deleted file mode 100644 index 9fdbde98..00000000 --- a/contracts/libraries/ECDSA.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/e0d2b3381847fd1b995e9842bae0672f3a99294c/contracts/cryptography/ECDSA.sol - -pragma solidity 0.7.5; - -/** - * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. - * - * These functions can be used to verify that a message was signed by the holder - * of the private keys of a given address. - */ -library ECDSA { - /** - * @dev Returns the address that signed a hashed message (`hash`) with - * `signature`. This address can then be used for verification purposes. - * - * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: - * this function rejects them by requiring the `s` value to be in the lower - * half order, and the `v` value to be either 27 or 28. - * - * IMPORTANT: `hash` _must_ be the result of a hash operation for the - * verification to be secure: it is possible to craft signatures that - * recover to arbitrary addresses for non-hashed data. A safe way to ensure - * this is by receiving a hash of the original message (which may otherwise - * be too long), and then calling {toEthSignedMessageHash} on it. - */ - function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { - // Check the signature length - if (signature.length != 65) { - revert("ECDSA: invalid signature length"); - } - - // Divide the signature in r, s and v variables - bytes32 r; - bytes32 s; - uint8 v; - - // ecrecover takes the signature parameters, and the only way to get them - // currently is to use assembly. - // solhint-disable-next-line no-inline-assembly - assembly { - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - } - - return recover(hash, v, r, s); - } - - /** - * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`, - * `r` and `s` signature fields separately. - */ - function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { - // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature - // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines - // the valid range for s in (281): 0 < s < secp256k1n รท 2 + 1, and for v in (282): v โˆˆ {27, 28}. Most - // signatures from current libraries generate a unique signature with an s-value in the lower half order. - // - // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value - // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or - // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept - // these malleable signatures as well. - require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's'"); - require(v == 27 || v == 28, "ECDSA: invalid signature 'v'"); - - // If the signature is valid (and not malleable), return the signer address - address signer = ecrecover(hash, v, r, s); - require(signer != address(0), "ECDSA: invalid signature"); - - return signer; - } - - /** - * @dev Returns an Ethereum Signed Message, created from a `hash`. This - * replicates the behavior of the - * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] - * JSON-RPC method. - * - * See {recover}. - */ - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); - } -} diff --git a/contracts/tokens/EIP712Upgradeable.sol b/contracts/tokens/EIP712Upgradeable.sol deleted file mode 100644 index ba9f968b..00000000 --- a/contracts/tokens/EIP712Upgradeable.sol +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: MIT -// Adopted from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/bcec9aaf09d00cc3420276aff2de8317156ede48/contracts/drafts/EIP712Upgradeable.sol - -pragma solidity 0.7.5; - -import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; - -/** - * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. - * - * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, - * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding - * they need in their contracts using a combination of `abi.encode` and `keccak256`. - * - * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding - * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA - * ({_hashTypedDataV4}). - * - * The implementation of the domain separator was designed to be as efficient as possible while still properly updating - * the chain id to protect against replay attacks on an eventual fork of the chain. - * - * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method - * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. - */ -abstract contract EIP712Upgradeable is Initializable { - /* solhint-disable var-name-mixedcase */ - bytes32 private _HASHED_NAME; - bytes32 private _HASHED_VERSION; - bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - /* solhint-enable var-name-mixedcase */ - - /** - * @dev Initializes the domain separator and parameter caches. - * - * The meaning of `name` and `version` is specified in - * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: - * - * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. - * - `version`: the current major version of the signing domain. - * - * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart - * contract upgrade]. - */ - // solhint-disable-next-line func-name-mixedcase - function __EIP712_init(string memory name, string memory version) internal initializer { - __EIP712_init_unchained(name, version); - } - - // solhint-disable-next-line func-name-mixedcase - function __EIP712_init_unchained(string memory name, string memory version) internal initializer { - bytes32 hashedName = keccak256(bytes(name)); - bytes32 hashedVersion = keccak256(bytes(version)); - _HASHED_NAME = hashedName; - _HASHED_VERSION = hashedVersion; - } - - /** - * @dev Returns the domain separator for the current chain. - */ - function _domainSeparatorV4() internal view returns (bytes32) { - return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash()); - } - - function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) { - return keccak256( - abi.encode( - typeHash, - name, - version, - _getChainId(), - address(this) - ) - ); - } - - /** - * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this - * function returns the hash of the fully encoded EIP712 message for this domain. - * - * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: - * - * ```solidity - * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( - * keccak256("Mail(address to,string contents)"), - * mailTo, - * keccak256(bytes(mailContents)) - * ))); - * address signer = ECDSA.recover(digest, signature); - * ``` - */ - function _hashTypedDataV4(bytes32 structHash) internal view returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash)); - } - - function _getChainId() private view returns (uint256 chainId) { - this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 - // solhint-disable-next-line no-inline-assembly - assembly { - chainId := chainid() - } - } - - /** - * @dev The hash of the name parameter for the EIP712 domain. - * - * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs - * are a concern. - */ - // solhint-disable-next-line func-name-mixedcase - function _EIP712NameHash() internal virtual view returns (bytes32) { - return _HASHED_NAME; - } - - /** - * @dev The hash of the version parameter for the EIP712 domain. - * - * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs - * are a concern. - */ - // solhint-disable-next-line func-name-mixedcase - function _EIP712VersionHash() internal virtual view returns (bytes32) { - return _HASHED_VERSION; - } - uint256[50] private __gap; -} diff --git a/contracts/tokens/ERC20PermitUpgradeable.sol b/contracts/tokens/ERC20PermitUpgradeable.sol index dcdc1dbb..58895c7d 100644 --- a/contracts/tokens/ERC20PermitUpgradeable.sol +++ b/contracts/tokens/ERC20PermitUpgradeable.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT -// Adopted from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/a4e82ad71d24cf5ab1778f1947441f24903da86a/contracts/drafts/ERC20PermitUpgradeable.sol +// Adopted from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0/contracts/drafts/ERC20PermitUpgradeable.sol pragma solidity 0.7.5; import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; -import "../interfaces/IERC20PermitUpgradeable.sol"; -import "../libraries/ECDSA.sol"; -import "./EIP712Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/drafts/IERC20PermitUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/drafts/EIP712Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol"; import "./ERC20Upgradeable.sol"; /** @@ -17,6 +17,8 @@ import "./ERC20Upgradeable.sol"; * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. + * + * _Available since v3.4._ */ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable { using CountersUpgradeable for CountersUpgradeable.Counter; @@ -45,7 +47,7 @@ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IER /** * @dev See {IERC20Permit-permit}. */ - function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override { + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override { // solhint-disable-next-line not-rely-on-time require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); @@ -54,7 +56,7 @@ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IER _PERMIT_TYPEHASH, owner, spender, - amount, + value, _nonces[owner].current(), deadline ) @@ -62,11 +64,11 @@ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IER bytes32 hash = _hashTypedDataV4(structHash); - address signer = ECDSA.recover(hash, v, r, s); + address signer = ECDSAUpgradeable.recover(hash, v, r, s); require(signer == owner, "ERC20Permit: invalid signature"); _nonces[owner].increment(); - _approve(owner, spender, amount); + _approve(owner, spender, value); } /** diff --git a/contracts/tokens/ERC20Upgradeable.sol b/contracts/tokens/ERC20Upgradeable.sol index 08078e45..934fc924 100644 --- a/contracts/tokens/ERC20Upgradeable.sol +++ b/contracts/tokens/ERC20Upgradeable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// Adopted from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0/contracts/token/ERC20/ERC20Upgradeable.sol +// Adopted from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0/contracts/token/ERC20/ERC20Upgradeable.sol pragma solidity 0.7.5; @@ -65,7 +65,7 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { /** * @dev Returns the name of the token. */ - function name() public view returns (string memory) { + function name() public view virtual returns (string memory) { return _name; } @@ -73,7 +73,7 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { * @dev Returns the symbol of the token, usually a shorter version of the * name. */ - function symbol() public view returns (string memory) { + function symbol() public view virtual returns (string memory) { return _symbol; } @@ -90,7 +90,7 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ - function decimals() public view returns (uint8) { + function decimals() public view virtual returns (uint8) { return _decimals; } @@ -141,8 +141,10 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); - if (sender != msg.sender && _allowances[sender][msg.sender] != uint256(-1)) { - _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: invalid amount")); + + uint256 currentAllowance = _allowances[sender][msg.sender]; + if (sender != msg.sender && currentAllowance != uint256(-1)) { + _approve(sender, msg.sender, currentAllowance.sub(amount)); } return true; } @@ -179,7 +181,7 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: invalid amount")); + _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue)); return true; } @@ -213,8 +215,8 @@ abstract contract ERC20Upgradeable is Initializable, IERC20Upgradeable { * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: invalid owner"); - require(spender != address(0), "ERC20: invalid spender"); + 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); diff --git a/contracts/tokens/StakeWiseToken.sol b/contracts/tokens/StakeWiseToken.sol new file mode 100644 index 00000000..9f18b866 --- /dev/null +++ b/contracts/tokens/StakeWiseToken.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "../presets/OwnablePausableUpgradeable.sol"; +import "./ERC20PermitUpgradeable.sol"; + +/** + * @title StakeWiseToken + */ +contract StakeWiseToken is OwnablePausableUpgradeable, ERC20PermitUpgradeable { + using SafeMathUpgradeable for uint256; + + mapping (address => uint256) private _balances; + + uint256 private _totalSupply; + + /** + * @dev Constructor for initializing the StakeWiseToken contract. + * @param _admin - address of the contract admin. + */ + function initialize(address _admin) external initializer { + __OwnablePausableUpgradeable_init(_admin); + __ERC20_init("StakeWise", "SWISE"); + __ERC20Permit_init("StakeWise"); + + uint256 totalMinted = 1_000_000_000 * 1e18; + _totalSupply = totalMinted; + _balances[_admin] = totalMinted; + emit Transfer(address(0), _admin, totalMinted); + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() external view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) external view override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {ERC20-_transfer}. + */ + function _transfer(address sender, address recipient, uint256 amount) internal override whenNotPaused { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] = _balances[sender].sub(amount); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } +} diff --git a/deployments/tokens.js b/deployments/tokens.js index 150c10c0..fdfc11e6 100644 --- a/deployments/tokens.js +++ b/deployments/tokens.js @@ -1,4 +1,4 @@ -const { ethers } = require('hardhat'); +const { ethers, upgrades } = require('hardhat'); async function deployAndInitializeERC20Mock( ownerAddress, @@ -12,6 +12,14 @@ async function deployAndInitializeERC20Mock( return erc20Mock.address; } +async function deployAndInitializeStakeWiseToken(adminAddress) { + const StakeWiseToken = await ethers.getContractFactory('StakeWiseToken'); + const proxy = await upgrades.deployProxy(StakeWiseToken, [adminAddress]); + await proxy.deployed(); + return proxy.address; +} + module.exports = { deployAndInitializeERC20Mock, + deployAndInitializeStakeWiseToken, }; From bc5f435fa1a72e34c386c70960881d4847767d94 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:00:37 +0300 Subject: [PATCH 02/25] Fix tests for latest mainnet fork --- .openzeppelin/unknown-31337.json | 378 +++++++++++++++++++++++++++++++ deployments/collectors.js | 43 ---- deployments/settings.js | 4 +- deployments/validators.js | 31 --- hardhat.config.js | 2 +- test/oracles/Oracles.test.js | 4 - test/pool/addDeposit.test.js | 24 +- test/pool/settings.test.js | 4 - 8 files changed, 395 insertions(+), 95 deletions(-) delete mode 100644 deployments/collectors.js delete mode 100644 deployments/validators.js diff --git a/.openzeppelin/unknown-31337.json b/.openzeppelin/unknown-31337.json index fe63059f..6bc6e536 100644 --- a/.openzeppelin/unknown-31337.json +++ b/.openzeppelin/unknown-31337.json @@ -722,6 +722,384 @@ } } } + }, + "1180315bf4492fa65c4efbff97aef9be398013d10c1aff681a175a03508864ae": { + "address": "0xc8970E7C07c251625F9F93cE510b1D9c1a08d299", + "txHash": "0x32c727ebb5dbbc1bd89a3a21cf15d9e74dbc688b404b60a286a9ae858c4967e2", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "Pool", + "label": "activatedValidators", + "type": "t_uint256", + "src": "contracts/collectors/Pool.sol:25" + }, + { + "contract": "Pool", + "label": "withdrawalCredentials", + "type": "t_bytes32", + "src": "contracts/collectors/Pool.sol:28" + }, + { + "contract": "Pool", + "label": "validatorRegistration", + "type": "t_contract(IDepositContract)5197", + "src": "contracts/collectors/Pool.sol:31" + }, + { + "contract": "Pool", + "label": "stakedEthToken", + "type": "t_contract(IStakedEthToken)5841", + "src": "contracts/collectors/Pool.sol:34" + }, + { + "contract": "Pool", + "label": "validators", + "type": "t_contract(IValidators)5889", + "src": "contracts/collectors/Pool.sol:37" + }, + { + "contract": "Pool", + "label": "oracles", + "type": "t_address", + "src": "contracts/collectors/Pool.sol:40" + }, + { + "contract": "Pool", + "label": "activations", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "src": "contracts/collectors/Pool.sol:43" + }, + { + "contract": "Pool", + "label": "pendingValidators", + "type": "t_uint256", + "src": "contracts/collectors/Pool.sol:46" + }, + { + "contract": "Pool", + "label": "minActivatingDeposit", + "type": "t_uint256", + "src": "contracts/collectors/Pool.sol:49" + }, + { + "contract": "Pool", + "label": "pendingValidatorsLimit", + "type": "t_uint256", + "src": "contracts/collectors/Pool.sol:52" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_contract(IDepositContract)5197": { + "label": "contract IDepositContract" + }, + "t_contract(IStakedEthToken)5841": { + "label": "contract IStakedEthToken" + }, + "t_contract(IValidators)5889": { + "label": "contract IValidators" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)1421_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)1421_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1156_storage" + } + ] + }, + "t_struct(Set)1156_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_bool": { + "label": "bool" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "88b20c6014e9428305028f15b31ccca5163a9836c693c992172ff3122e72bab3": { + "address": "0x749dCCE12E8337dfb44635082519E656d44A2672", + "txHash": "0xf7667e029d07c1ee47a46235bb1e90082142d982a10fa94d95fa919594f07c2f", + "layout": { + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:25" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol:30" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol:37" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:31" + }, + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:28" + }, + { + "contract": "PausableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol:96" + }, + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)39_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61" + }, + { + "contract": "AccessControlUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:225" + }, + { + "contract": "Oracles", + "label": "syncPeriod", + "type": "t_uint256", + "src": "contracts/Oracles.sol:26" + }, + { + "contract": "Oracles", + "label": "candidates", + "type": "t_mapping(t_bytes32,t_uint256)", + "src": "contracts/Oracles.sol:29" + }, + { + "contract": "Oracles", + "label": "rewardEthUniswapPairs", + "type": "t_array(t_address)dyn_storage", + "src": "contracts/Oracles.sol:32" + }, + { + "contract": "Oracles", + "label": "submittedVotes", + "type": "t_mapping(t_bytes32,t_bool)", + "src": "contracts/Oracles.sol:35" + }, + { + "contract": "Oracles", + "label": "rewardEthToken", + "type": "t_contract(IRewardEthToken)5683", + "src": "contracts/Oracles.sol:38" + }, + { + "contract": "Oracles", + "label": "nonce", + "type": "t_struct(Counter)1105_storage", + "src": "contracts/Oracles.sol:41" + }, + { + "contract": "Oracles", + "label": "pool", + "type": "t_contract(IPool)5564", + "src": "contracts/Oracles.sol:44" + } + ], + "types": { + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_array(t_address)dyn_storage": { + "label": "address[]" + }, + "t_address": { + "label": "address" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)" + }, + "t_bool": { + "label": "bool" + }, + "t_contract(IRewardEthToken)5683": { + "label": "contract IRewardEthToken" + }, + "t_struct(Counter)1105_storage": { + "label": "struct CountersUpgradeable.Counter", + "members": [ + { + "label": "_value", + "type": "t_uint256" + } + ] + }, + "t_contract(IPool)5564": { + "label": "contract IPool" + }, + "t_mapping(t_bytes32,t_struct(RoleData)39_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)" + }, + "t_struct(RoleData)39_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_struct(AddressSet)1421_storage" + }, + { + "label": "adminRole", + "type": "t_bytes32" + } + ] + }, + "t_struct(AddressSet)1421_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)1156_storage" + } + ] + }, + "t_struct(Set)1156_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)" + } + ] + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } } }, "admin": { diff --git a/deployments/collectors.js b/deployments/collectors.js deleted file mode 100644 index 6d2c0e15..00000000 --- a/deployments/collectors.js +++ /dev/null @@ -1,43 +0,0 @@ -const hre = require('hardhat'); -const { - getProxyAdminFactory, -} = require('@openzeppelin/hardhat-upgrades/dist/proxy-factory'); - -async function preparePoolUpgradeData( - oraclesContractAddress, - activatedValidators, - pendingValidators, - minActivatingDeposit, - pendingValidatorsLimit -) { - const Pool = await hre.ethers.getContractFactory('Pool'); - return Pool.interface.encodeFunctionData('upgrade', [ - oraclesContractAddress, - activatedValidators, - pendingValidators, - minActivatingDeposit, - pendingValidatorsLimit, - ]); -} - -async function upgradePool( - adminAddress, - proxyAdminContractAddress, - poolContractAddress, - nextImplementation, - data -) { - const signer = await hre.ethers.provider.getSigner(adminAddress); - const AdminFactory = await getProxyAdminFactory(hre); - const proxyAdmin = AdminFactory.attach(proxyAdminContractAddress); - - const proxy = await proxyAdmin - .connect(signer) - .upgradeAndCall(poolContractAddress, nextImplementation, data); - return proxy.address; -} - -module.exports = { - upgradePool, - preparePoolUpgradeData, -}; diff --git a/deployments/settings.js b/deployments/settings.js index 90d27af2..10f7a09a 100644 --- a/deployments/settings.js +++ b/deployments/settings.js @@ -37,8 +37,8 @@ if (hre.hardhatArguments && hre.hardhatArguments.network === 'goerli') { VRC: '0x00000000219ab540356cbb839cbe05303d7705fa', withdrawalCredentials: '0x003e294ffc37978496f1b9298d5984ad4d55d4e2d1e6a06ee6904810c7b9e0d5', - activatedValidators: '530', - pendingValidators: '2', + activatedValidators: '569', + pendingValidators: '1', minActivatingDeposit: '32000000000000000000', // 32 ETH pendingValidatorsLimit: '500', // 5 % }; diff --git a/deployments/validators.js b/deployments/validators.js deleted file mode 100644 index d8dfc197..00000000 --- a/deployments/validators.js +++ /dev/null @@ -1,31 +0,0 @@ -const hre = require('hardhat'); -const { - getProxyAdminFactory, -} = require('@openzeppelin/hardhat-upgrades/dist/proxy-factory'); - -async function prepareOraclesUpgradeData(poolContractAddress) { - const Oracles = await hre.ethers.getContractFactory('Oracles'); - return Oracles.interface.encodeFunctionData('upgrade', [poolContractAddress]); -} - -async function upgradeOracles( - adminAddress, - proxyAdminContractAddress, - oraclesContractAddress, - nextImplementation, - data -) { - const signer = await hre.ethers.provider.getSigner(adminAddress); - const AdminFactory = await getProxyAdminFactory(hre); - const proxyAdmin = AdminFactory.attach(proxyAdminContractAddress); - - const proxy = await proxyAdmin - .connect(signer) - .upgradeAndCall(oraclesContractAddress, nextImplementation, data); - return proxy.address; -} - -module.exports = { - prepareOraclesUpgradeData, - upgradeOracles, -}; diff --git a/hardhat.config.js b/hardhat.config.js index a514adb4..adf865d1 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -11,7 +11,7 @@ require('hardhat-abi-exporter'); require('@nomiclabs/hardhat-etherscan'); const GAS_PRICE = 20e9; // 20 Gwei -const BLOCK_NUMBER = 12007856; +const BLOCK_NUMBER = 12122895; const OPTIMIZER_RUNS = 5000000; const log = (...text) => console.log(gray(...['โ””โ”€> [DEBUG]'].concat(text))); diff --git a/test/oracles/Oracles.test.js b/test/oracles/Oracles.test.js index d7a61420..5a0d9333 100644 --- a/test/oracles/Oracles.test.js +++ b/test/oracles/Oracles.test.js @@ -27,10 +27,6 @@ contract('Oracles', ([_, ...accounts]) => { after(async () => stopImpersonatingAccount(admin)); beforeEach(async () => { - // update contract settings before upgrade - contractSettings.activatedValidators = '10'; - contractSettings.pendingValidators = '2'; - await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); diff --git a/test/pool/addDeposit.test.js b/test/pool/addDeposit.test.js index fe125e9d..aeb07f34 100644 --- a/test/pool/addDeposit.test.js +++ b/test/pool/addDeposit.test.js @@ -37,10 +37,6 @@ contract('Pool (add deposit)', ([sender1, sender2, sender3, operator]) => { after(async () => stopImpersonatingAccount(admin)); beforeEach(async () => { - // update contract settings before upgrade - contractSettings.activatedValidators = '10'; - contractSettings.pendingValidators = '0'; - // reset contract settings activatedValidators = new BN(contractSettings.activatedValidators); pendingValidators = new BN(contractSettings.pendingValidators); @@ -123,13 +119,15 @@ contract('Pool (add deposit)', ([sender1, sender2, sender3, operator]) => { }); it('places deposit of user to the activation queue with exceeded pending validators limit', async () => { - await pool.setPendingValidatorsLimit('1000', { from: admin }); // 10 % + await pool.setPendingValidatorsLimit('1', { from: admin }); // 0.01 % await pool.setMinActivatingDeposit(ether('0.01'), { from: admin }); - // deposit more than 10 % + // deposit more than 0.01 % let depositAmount = ether('32').mul(new BN(2)); poolBalance = poolBalance.add(depositAmount); - let validatorIndex = activatedValidators.add(new BN(2)); + let validatorIndex = activatedValidators + .add(pendingValidators) + .add(new BN(2)); // check deposit amount placed in activation queue let receipt = await pool.addDeposit({ @@ -197,7 +195,9 @@ contract('Pool (add deposit)', ([sender1, sender2, sender3, operator]) => { from: sender1, value: depositAmount, }); - validatorIndex = activatedValidators.add(new BN(1)); + validatorIndex = activatedValidators + .add(pendingValidators) + .add(new BN(1)); let validators = await Validators.at(contracts.validators); await validators.addOperator(operator, { from: admin }); @@ -299,13 +299,17 @@ contract('Pool (add deposit)', ([sender1, sender2, sender3, operator]) => { from: sender3, value: depositAmount, }); - validatorIndex1 = activatedValidators.add(new BN(1)); + validatorIndex1 = activatedValidators + .add(pendingValidators) + .add(new BN(1)); await pool.addDeposit({ from: sender3, value: depositAmount, }); - validatorIndex2 = activatedValidators.add(new BN(2)); + validatorIndex2 = activatedValidators + .add(pendingValidators) + .add(new BN(2)); let validators = await Validators.at(contracts.validators); await validators.addOperator(operator, { from: admin }); diff --git a/test/pool/settings.test.js b/test/pool/settings.test.js index 861be43e..35373a2b 100644 --- a/test/pool/settings.test.js +++ b/test/pool/settings.test.js @@ -27,10 +27,6 @@ contract('Pool (settings)', ([anyone]) => { after(async () => stopImpersonatingAccount(admin)); beforeEach(async () => { - // update contract settings before upgrade - contractSettings.activatedValidators = '10'; - contractSettings.pendingValidators = '2'; - await impersonateAccount(admin); await send.ether(anyone, admin, ether('5')); await upgradeContracts(); From 2bc70e977c0bdcd102e4c9c03f79c3e8a79aedb5 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:01:59 +0300 Subject: [PATCH 03/25] Add tests for mainnet proxies --- test/Proxies.test.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test/Proxies.test.js diff --git a/test/Proxies.test.js b/test/Proxies.test.js new file mode 100644 index 00000000..b5dab362 --- /dev/null +++ b/test/Proxies.test.js @@ -0,0 +1,46 @@ +const hre = require('hardhat'); +const { contractSettings, contracts } = require('../deployments/settings'); + +let proxies = [ + contracts.pool, + contracts.validators, + contracts.stakedEthToken, + contracts.rewardEthToken, + contracts.oracles, +]; + +let implementations = [ + '0xc8970E7C07c251625F9F93cE510b1D9c1a08d299', + '0xa34E1010E2b76abdf7399E6C88147D0FAfE28e90', + '0x6A8a1716a44f700af56ea52D44B916A50333A369', + '0x46B7232bc7392b157371eBFcD4618Ca9CEedb1bd', + '0x749dCCE12E8337dfb44635082519E656d44A2672', +]; + +contract('Proxies', () => { + let proxyAdmin; + + beforeEach(async () => { + proxyAdmin = await hre.upgrades.admin.getInstance(); + }); + + it('proxy admin is set correctly', async () => { + for (const proxy of proxies) { + expect(await proxyAdmin.getProxyAdmin(proxy)).to.equal( + proxyAdmin.address + ); + } + }); + + it('proxy implementation is correct', async () => { + for (let i = 0; i < proxies.length; i++) { + expect(await proxyAdmin.getProxyImplementation(proxies[i])).to.equal( + implementations[i] + ); + } + }); + + it('proxy admin admin owner is DAO', async () => { + expect(await proxyAdmin.owner()).to.equal(contractSettings.admin); + }); +}); From e22269384c20f37c263b22fb89726c805958bc91 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:43:23 +0300 Subject: [PATCH 04/25] Update deployments to deploy only StakeWise token --- deployments/index.js | 76 ++++++-------------------------------------- 1 file changed, 10 insertions(+), 66 deletions(-) diff --git a/deployments/index.js b/deployments/index.js index e5e18f79..f42bd9b8 100644 --- a/deployments/index.js +++ b/deployments/index.js @@ -1,9 +1,7 @@ const hre = require('hardhat'); const { white, green } = require('chalk'); const { contractSettings, contracts } = require('./settings'); -const { prepareOraclesUpgradeData, upgradeOracles } = require('./validators'); -const { preparePoolUpgradeData, upgradePool } = require('./collectors'); -const { prepareUpgrade } = require('./utils'); +const { deployAndInitializeStakeWiseToken } = require('./tokens'); function log(message) { if (hre.config != null && hre.config.suppressLogs !== true) { @@ -12,81 +10,27 @@ function log(message) { } async function prepareContractsUpgrades() { - const Pool = await hre.ethers.getContractFactory('Pool'); - const poolImplementation = await prepareUpgrade(Pool, contracts.pool); - log( - white(`Deployed Pool implementation contract: ${green(poolImplementation)}`) - ); - - const poolUpgradeData = await preparePoolUpgradeData( - contracts.oracles, - contractSettings.activatedValidators, - contractSettings.pendingValidators, - contractSettings.minActivatingDeposit, - contractSettings.pendingValidatorsLimit - ); - log(white(`Pool upgrade data: ${green(poolUpgradeData)}`)); + log(white('Nothing to prepare...')); +} - const Oracles = await hre.ethers.getContractFactory('Oracles'); - const oraclesImplementation = await prepareUpgrade( - Oracles, - contracts.oracles +async function upgradeContracts() { + let stakeWiseTokenContractAddress = await deployAndInitializeStakeWiseToken( + contractSettings.admin ); log( white( - `Deployed Oracles implementation contract: ${green( - oraclesImplementation + `Deployed StakeWise token contract: ${green( + stakeWiseTokenContractAddress )}` ) ); - const oraclesUpgradeData = await prepareOraclesUpgradeData(contracts.pool); - log(white(`Oracles upgrade data: ${green(oraclesUpgradeData)}`)); - return { - poolImplementation, - poolUpgradeData, - oraclesImplementation, - oraclesUpgradeData, + ...contracts, + stakeWiseToken: stakeWiseTokenContractAddress, }; } -async function upgradeContracts() { - let preparedUpgrades = await prepareContractsUpgrades(); - const signer = await hre.ethers.provider.getSigner(contractSettings.admin); - - const Pool = await hre.ethers.getContractFactory('Pool'); - let pool = Pool.attach(contracts.pool); - await pool.connect(signer).pause(); - await pool.connect(signer).addAdmin(contracts.proxyAdmin); - - const Oracles = await hre.ethers.getContractFactory('Oracles'); - let oracles = Oracles.attach(contracts.oracles); - await oracles.connect(signer).pause(); - await oracles.connect(signer).addAdmin(contracts.proxyAdmin); - - await upgradePool( - contractSettings.admin, - contracts.proxyAdmin, - contracts.pool, - preparedUpgrades.poolImplementation, - preparedUpgrades.poolUpgradeData - ); - log(white('Upgraded Pool contract')); - - await upgradeOracles( - contractSettings.admin, - contracts.proxyAdmin, - contracts.oracles, - preparedUpgrades.oraclesImplementation, - preparedUpgrades.oraclesUpgradeData - ); - log(white('Upgraded Oracles contract')); - - await pool.connect(signer).unpause(); - await oracles.connect(signer).unpause(); -} - module.exports = { prepareContractsUpgrades, upgradeContracts, From fa7ac6eaa0b93262ec58d91c72b01384ff1193da Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:44:23 +0300 Subject: [PATCH 05/25] Upgrade packages to latest version --- package.json | 26 +- yarn.lock | 828 ++++++++++++++++++++++++++++----------------------- 2 files changed, 474 insertions(+), 380 deletions(-) diff --git a/package.json b/package.json index 2322cfc5..9de4137f 100644 --- a/package.json +++ b/package.json @@ -46,31 +46,31 @@ }, "devDependencies": { "@codechecks/client": "^0.1.10", - "@nomiclabs/hardhat-ethers": "^2.0.1", + "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-etherscan": "^2.1.1", "@nomiclabs/hardhat-truffle5": "^2.0.0", "@nomiclabs/hardhat-web3": "^2.0.0", - "@openzeppelin/contracts": "^3.3.0", - "@openzeppelin/contracts-upgradeable": "^3.3.0", + "@openzeppelin/contracts": "3.3.0", + "@openzeppelin/contracts-upgradeable": "3.4.1", "@openzeppelin/hardhat-upgrades": "^1.6.0", "@openzeppelin/test-helpers": "^0.5.10", - "chai": "^4.3.0", + "chai": "^4.3.4", "chalk": "^4.1.0", - "eslint": "^7.20.0", + "eslint": "^7.23.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", "eth-sig-util": "^3.0.1", - "ethereumjs-util": "^7.0.8", - "ethers": "^5.0.31", - "hardhat": "^2.0.11", - "hardhat-abi-exporter": "^2.0.8", - "hardhat-contract-sizer": "^2.0.2", + "ethereumjs-util": "^7.0.9", + "ethers": "^5.0.32", + "hardhat": "^2.1.2", + "hardhat-abi-exporter": "^2.2.0", + "hardhat-contract-sizer": "^2.0.3", "hardhat-gas-reporter": "^1.0.4", - "husky": "^5.1.1", + "husky": "^5.2.0", "lint-staged": "10.5.4", "prettier": "^2.2.1", - "solhint": "^3.3.2", - "solidity-coverage": "^0.7.15", + "solhint": "^3.3.4", + "solidity-coverage": "^0.7.16", "web3": "^1.3.4" }, "dependencies": {} diff --git a/yarn.lock b/yarn.lock index a10ebc40..c74668ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,9 +22,9 @@ integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.8.tgz#10b2dac78526424dfc1f47650d0e415dfd9dc481" - integrity sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw== + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" + integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== dependencies: "@babel/helper-validator-identifier" "^7.12.11" chalk "^2.0.0" @@ -91,10 +91,10 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.0.12", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.10", "@ethersproject/abi@^5.0.2": - version "5.0.12" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.12.tgz#9aebe6aedc05ce45bb6c41b06d80bd195b7de77c" - integrity sha512-Ujr/3bwyYYjXLDQfebeiiTuvOw9XtUKM8av6YkoBeMXyGQM9GkjrQlwJMNwGTmqjATH/ZNbRgCh98GjOLiIB1Q== +"@ethersproject/abi@5.0.13", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.0.10", "@ethersproject/abi@^5.0.2": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.13.tgz#600a559c3730467716595658beaa2894b4352bcc" + integrity sha512-2coOH3D7ra1lwamKEH0HVc+Jbcsw5yfeCgmY8ekhCDualEiyyovD2qDcMBBcY3+kjoLHVTmo7ost6MNClxdOrg== dependencies: "@ethersproject/address" "^5.0.9" "@ethersproject/bignumber" "^5.0.13" @@ -121,10 +121,10 @@ "@ethersproject/properties" "^5.0.3" "@ethersproject/strings" "^5.0.4" -"@ethersproject/abstract-provider@5.0.9", "@ethersproject/abstract-provider@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.9.tgz#a55410b73e3994842884eb82b1f43e3a9f653eea" - integrity sha512-X9fMkqpeu9ayC3JyBkeeZhn35P4xQkpGX/l+FrxDtEW9tybf/UWXSMi8bGThpPtfJ6q6U2LDetXSpSwK4TfYQQ== +"@ethersproject/abstract-provider@5.0.10", "@ethersproject/abstract-provider@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.10.tgz#a533aed39a5f27312745c8c4c40fa25fc884831c" + integrity sha512-OSReY5iz94iIaPlRvLiJP8YVIvQLx4aUvMMnHWSaA/vTU8QHZmgNlt4OBdYV1+aFY8Xl+VRYiWBHq72ZDKXXCQ== dependencies: "@ethersproject/bignumber" "^5.0.13" "@ethersproject/bytes" "^5.0.9" @@ -134,10 +134,10 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/web" "^5.0.12" -"@ethersproject/abstract-signer@5.0.13", "@ethersproject/abstract-signer@^5.0.10": - version "5.0.13" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.13.tgz#59b4d0367d6327ec53bc269c6730c44a4a3b043c" - integrity sha512-VBIZEI5OK0TURoCYyw0t3w+TEO4kdwnI9wvt4kqUwyxSn3YCRpXYVl0Xoe7XBR/e5+nYOi2MyFGJ3tsFwONecQ== +"@ethersproject/abstract-signer@5.0.14", "@ethersproject/abstract-signer@^5.0.10": + version "5.0.14" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.14.tgz#30ef912b0f86599d90fdffc65c110452e7b55cf1" + integrity sha512-JztBwVO7o5OHLh2vyjordlS4/1EjRyaECtc8vPdXTF1i4dXN+J0coeRoPN6ZFbBvi/YbaB6br2fvqhst1VQD/g== dependencies: "@ethersproject/abstract-provider" "^5.0.8" "@ethersproject/bignumber" "^5.0.13" @@ -145,10 +145,10 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" -"@ethersproject/address@5.0.10", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.9": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.10.tgz#2bc69fdff4408e0570471cd19dee577ab06a10d0" - integrity sha512-70vqESmW5Srua1kMDIN6uVfdneZMaMyRYH4qPvkAXGkbicrCOsA9m01vIloA4wYiiF+HLEfL1ENKdn5jb9xiAw== +"@ethersproject/address@5.0.11", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.9": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.11.tgz#12022e8c590c33939beb5ab18b401ecf585eac59" + integrity sha512-Et4GBdD8/tsBGjCEOKee9upN29qjL5kbRcmJifb4Penmiuh9GARXL2/xpXvEp5EW+EIW/rfCHFJrkYBgoQFQBw== dependencies: "@ethersproject/bignumber" "^5.0.13" "@ethersproject/bytes" "^5.0.9" @@ -156,48 +156,48 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/rlp" "^5.0.7" -"@ethersproject/base64@5.0.8", "@ethersproject/base64@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.8.tgz#1bc4b4b8c59c1debf972c7164b96c0b8964a20a1" - integrity sha512-PNbpHOMgZpZ1skvQl119pV2YkCPXmZTxw+T92qX0z7zaMFPypXWTZBzim+hUceb//zx4DFjeGT4aSjZRTOYThg== +"@ethersproject/base64@5.0.9", "@ethersproject/base64@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.9.tgz#bb1f35d3dba92082a574d5e2418f9202a0a1a7e6" + integrity sha512-37RBz5LEZ9SlTNGiWCYFttnIN9J7qVs9Xo2EbqGqDH5LfW9EIji66S+YDMpXVo1zWDax1FkEldAoatxHK2gfgA== dependencies: "@ethersproject/bytes" "^5.0.9" -"@ethersproject/basex@5.0.8", "@ethersproject/basex@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.8.tgz#6867fad20047aa29fbd4b880f27894ed04cc7bb8" - integrity sha512-PCVKZIShBQUqAXjJSvaCidThPvL0jaaQZcewJc0sf8Xx05BizaOS8r3jdPdpNdY+/qZtRDqwHTSKjvR/xssyLQ== +"@ethersproject/basex@5.0.9", "@ethersproject/basex@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.9.tgz#00d727a031bac563cb8bb900955206f1bf3cf1fc" + integrity sha512-FANswl1IN3PS0eltQxH2aM2+utPrkLUVG4XVFi6SafRG9EpAqXCgycxC8PU90mPGhigYTpg9cnTB5mCZ6ejQjw== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/properties" "^5.0.7" -"@ethersproject/bignumber@5.0.14", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.13", "@ethersproject/bignumber@^5.0.7": - version "5.0.14" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.14.tgz#605bc61dcbd4a8c6df8b5a7a77c0210273f3de8a" - integrity sha512-Q4TjMq9Gg3Xzj0aeJWqJgI3tdEiPiET7Y5OtNtjTAODZ2kp4y9jMNg97zVcvPedFvGROdpGDyCI77JDFodUzOw== +"@ethersproject/bignumber@5.0.15", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.13", "@ethersproject/bignumber@^5.0.7": + version "5.0.15" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.15.tgz#b089b3f1e0381338d764ac1c10512f0c93b184ed" + integrity sha512-MTADqnyacvdRwtKh7o9ujwNDSM1SDJjYDMYAzjIgjoi9rh6TY4suMbhCa3i2vh3SUXiXSICyTI8ui+NPdrZ9Lw== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" bn.js "^4.4.0" -"@ethersproject/bytes@5.0.10", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.9": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.10.tgz#aa49afe7491ba24ff76fa33d98677351263f9ba4" - integrity sha512-vpu0v1LZ1j1s9kERQIMnVU69MyHEzUff7nqK9XuCU4vx+AM8n9lU2gj7jtJIvGSt9HzatK/6I6bWusI5nyuaTA== +"@ethersproject/bytes@5.0.11", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.9": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.11.tgz#21118e75b1d00db068984c15530e316021101276" + integrity sha512-D51plLYY5qF05AsoVQwIZVLqlBkaTPVHVP/1WmmBIWyHB0cRW0C9kh0kx5Exo51rB63Hk8PfHxc7SmpoaQFEyg== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/constants@5.0.9", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.9.tgz#81ac44c3bf612de63eb1c490b314ea1b932cda9f" - integrity sha512-2uAKH89UcaJP/Sc+54u92BtJtZ4cPgcS1p0YbB1L3tlkavwNvth+kNCUplIB1Becqs7BOZr0B/3dMNjhJDy4Dg== +"@ethersproject/constants@5.0.10", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.10.tgz#eb0c604fbc44c53ba9641eed31a1d0c9e1ebcadc" + integrity sha512-OSo8jxkHLDXieCy8bgOFR7lMfgPxEzKvSDdP+WAWHCDM8+orwch0B6wzkTmiQFgryAtIctrBt5glAdJikZ3hGw== dependencies: "@ethersproject/bignumber" "^5.0.13" -"@ethersproject/contracts@5.0.11": - version "5.0.11" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.11.tgz#e6cc57698a05be2329cb2ca3d7e87686f95e438a" - integrity sha512-FTUUd/6x00dYL2VufE2VowZ7h3mAyBfCQMGwI3tKDIWka+C0CunllFiKrlYCdiHFuVeMotR65dIcnzbLn72MCw== +"@ethersproject/contracts@5.0.12": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.12.tgz#6d488db46221258399dfe80b89bf849b3afd7897" + integrity sha512-srijy31idjz8bE+gL1I6IRj2H4I9dUwfQ+QroLrIgNdGArqY8y2iFUKa3QTy+JBX26fJsdYiCQi1kKkaNpnMpQ== dependencies: "@ethersproject/abi" "^5.0.10" "@ethersproject/abstract-provider" "^5.0.8" @@ -209,10 +209,10 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" -"@ethersproject/hash@5.0.11", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.10", "@ethersproject/hash@^5.0.4": - version "5.0.11" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.11.tgz#da89517438bbbf8a39df56fff09f0a71669ae7a7" - integrity sha512-H3KJ9fk33XWJ2djAW03IL7fg3DsDMYjO1XijiUb1hJ85vYfhvxu0OmsU7d3tg2Uv1H1kFSo8ghr3WFQ8c+NL3g== +"@ethersproject/hash@5.0.12", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.10", "@ethersproject/hash@^5.0.4": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.12.tgz#1074599f7509e2ca2bb7a3d4f4e39ab3a796da42" + integrity sha512-kn4QN+fhNFbUgX3XZTZUaQixi0oyfIEY+hfW+KtkHu+rq7dV76oAIvaLEEynu1/4npOL38E4X4YI42gGZk+C0Q== dependencies: "@ethersproject/abstract-signer" "^5.0.10" "@ethersproject/address" "^5.0.9" @@ -223,10 +223,10 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/hdnode@5.0.9", "@ethersproject/hdnode@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.9.tgz#ce65b430d3d3f0cd3c8f9dfaaf376b55881d9dba" - integrity sha512-S5UMmIC6XfFtqhUK4uTjD8GPNzSbE+sZ/0VMqFnA3zAJ+cEFZuEyhZDYnl2ItGJzjT4jsy+uEy1SIl3baYK1PQ== +"@ethersproject/hdnode@5.0.10", "@ethersproject/hdnode@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.10.tgz#f7cdf154bf5d104c76dce2940745fc71d9e7eb1b" + integrity sha512-ZLwMtIcXK7xz2lSITDCl40W04CtRq4K9NwBxhCzdzPdaz6XnoJMwGz2YMVLg+8ksseq+RYtTwIIXtlK6vyvQyg== dependencies: "@ethersproject/abstract-signer" "^5.0.10" "@ethersproject/basex" "^5.0.7" @@ -241,10 +241,10 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/json-wallets@5.0.11", "@ethersproject/json-wallets@^5.0.10": - version "5.0.11" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.11.tgz#86fdc41b7762acb443d6a896f6c61231ab2aee5d" - integrity sha512-0GhWScWUlXXb4qJNp0wmkU95QS3YdN9UMOfMSEl76CRANWWrmyzxcBVSXSBu5iQ0/W8wO+xGlJJ3tpA6v3mbIw== +"@ethersproject/json-wallets@5.0.12", "@ethersproject/json-wallets@^5.0.10": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.12.tgz#8946a0fcce1634b636313a50330b7d30a24996e8" + integrity sha512-nac553zGZnOewpjlqbfy7WBl8m3y7qudzRsI2dCxrediYtPIVIs9f6Pbnou8vDmmp8X4/U4W788d+Ma88o+Gbg== dependencies: "@ethersproject/abstract-signer" "^5.0.10" "@ethersproject/address" "^5.0.9" @@ -260,45 +260,45 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.0.8", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.8.tgz#13aaf69e1c8bd15fc59a2ebd055c0878f2a059c8" - integrity sha512-zoGbwXcWWs9MX4NOAZ7N0hhgIRl4Q/IO/u9c/RHRY4WqDy3Ywm0OLamEV53QDwhjwn3YiiVwU1Ve5j7yJ0a/KQ== +"@ethersproject/keccak256@5.0.9", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.9.tgz#ca0d86e4af56c13b1ef25e533bde3e96d28f647d" + integrity sha512-zhdUTj6RGtCJSgU+bDrWF6cGbvW453LoIC1DSNWrTlXzC7WuH4a+EiPrgc7/kNoRxerKuA/cxYlI8GwNtVtDlw== dependencies: "@ethersproject/bytes" "^5.0.9" js-sha3 "0.5.7" -"@ethersproject/logger@5.0.9", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.9.tgz#0e6a0b3ecc938713016954daf4ac7967467aa763" - integrity sha512-kV3Uamv3XOH99Xf3kpIG3ZkS7mBNYcLDM00JSDtNgNB4BihuyxpQzIZPRIDmRi+95Z/R1Bb0X2kUNHa/kJoVrw== +"@ethersproject/logger@5.0.10", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.10.tgz#fd884688b3143253e0356ef92d5f22d109d2e026" + integrity sha512-0y2T2NqykDrbPM3Zw9RSbPkDOxwChAL8detXaom76CfYoGxsOnRP/zTX8OUAV+x9LdwzgbWvWmeXrc0M7SuDZw== -"@ethersproject/networks@5.0.8", "@ethersproject/networks@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.8.tgz#37e6f8c058f2d540373ea5939056cd3de069132e" - integrity sha512-PYpptlO2Tu5f/JEBI5hdlMds5k1DY1QwVbh3LKPb3un9dQA2bC51vd2/gRWAgSBpF3kkmZOj4FhD7ATLX4H+DA== +"@ethersproject/networks@5.0.9", "@ethersproject/networks@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.9.tgz#ec5da11e4d4bfd69bec4eaebc9ace33eb9569279" + integrity sha512-L8+VCQwArBLGkxZb/5Ns/OH/OxP38AcaveXIxhUTq+VWpXYjrObG3E7RDQIKkUx1S1IcQl/UWTz5w4DK0UitJg== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/pbkdf2@5.0.8", "@ethersproject/pbkdf2@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.8.tgz#06a086b1ac04c75e6846afd6cf6170a49a634411" - integrity sha512-UlmAMGbIPaS2xXsI38FbePVTfJMuU9jnwcqVn3p88HxPF4kD897ha+l3TNsBqJqf32UbQL5GImnf1oJkSKq4vQ== +"@ethersproject/pbkdf2@5.0.9", "@ethersproject/pbkdf2@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.9.tgz#be39c7f0a66c0d3cb1ad1dbb12a78e9bcdf9b5ae" + integrity sha512-ItE/wQ/WVw/ajEHPUVgfu0aEvksPgOQc+278bke8sGKnGO3ppjmqp0MHh17tHc1EBTzJbSms5aLIqc56qZ/oiA== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/sha2" "^5.0.7" -"@ethersproject/properties@5.0.8", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.8.tgz#e45d28d25402c73394873dbf058f856c966cae01" - integrity sha512-zEnLMze2Eu2VDPj/05QwCwMKHh506gpT9PP9KPVd4dDB+5d6AcROUYVLoIIQgBYK7X/Gw0UJmG3oVtnxOQafAw== +"@ethersproject/properties@5.0.9", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.9.tgz#d7aae634680760136ea522e25c3ef043ec15b5c2" + integrity sha512-ZCjzbHYTw+rF1Pn8FDCEmx3gQttwIHcm/6Xee8g/M3Ga3SfW4tccNMbs5zqnBH0E4RoOPaeNgyg1O68TaF0tlg== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/providers@5.0.23": - version "5.0.23" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.23.tgz#1e26512303d60bbd557242532fdb5fa3c5d5fb73" - integrity sha512-eJ94z2tgPaUgUmxwd3BVkIzkgkbNIkY6wRPVas04LVaBTycObQbgj794aaUu2bfk7+Bn2B/gjUZtJW1ybxh9/A== +"@ethersproject/providers@5.0.24": + version "5.0.24" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.24.tgz#4c638a029482d052faa18364b5e0e2d3ddd9c0cb" + integrity sha512-M4Iw1r4gGJkt7ZUa++iREuviKL/DIpmIMsaUlVlXtV+ZrUXeN8xQ3zOTrbz7R4h9W9oljBZM7i4D3Kn1krJ30A== dependencies: "@ethersproject/abstract-provider" "^5.0.8" "@ethersproject/abstract-signer" "^5.0.10" @@ -320,45 +320,45 @@ bech32 "1.1.4" ws "7.2.3" -"@ethersproject/random@5.0.8", "@ethersproject/random@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.8.tgz#8d3726be48e95467abce9b23c93adbb1de009dda" - integrity sha512-4rHtotmd9NjklW0eDvByicEkL+qareIyFSbG1ShC8tPJJSAC0g55oQWzw+3nfdRCgBHRuEE7S8EcPcTVPvZ9cA== +"@ethersproject/random@5.0.9", "@ethersproject/random@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.9.tgz#1903d4436ba66e4c8ac77968b16f756abea3a0d0" + integrity sha512-DANG8THsKqFbJOantrxumtG6gyETNE54VfbsWa+SQAT8WKpDo9W/X5Zhh73KuhClaey1UI32uVmISZeq/Zxn1A== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" -"@ethersproject/rlp@5.0.8", "@ethersproject/rlp@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.8.tgz#ff54e206d0ae28640dd054f2bcc7070f06f9dfbe" - integrity sha512-E4wdFs8xRNJfzNHmnkC8w5fPeT4Wd1U2cust3YeT16/46iSkLT8nn8ilidC6KhR7hfuSZE4UqSPzyk76p7cdZg== +"@ethersproject/rlp@5.0.9", "@ethersproject/rlp@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.9.tgz#da205bf8a34d3c3409eb73ddd237130a4b376aff" + integrity sha512-ns1U7ZMVeruUW6JXc4om+1w3w4ynHN/0fpwmeNTsAjwGKoF8SAUgue6ylKpHKWSti2idx7jDxbn8hNNFHk67CA== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" -"@ethersproject/sha2@5.0.8", "@ethersproject/sha2@^5.0.7": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.8.tgz#9903c67e562739d8b312820b0a265b9c9bf35fc3" - integrity sha512-ILP1ZgyvDj4rrdE+AXrTv9V88m7x87uga2VZ/FeULKPumOEw/4bGnJz/oQ8zDnDvVYRCJ+48VaQBS2CFLbk1ww== +"@ethersproject/sha2@5.0.9", "@ethersproject/sha2@^5.0.7": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.9.tgz#41275ee03e6e1660b3c997754005e089e936adc6" + integrity sha512-5FH4s47gM7N1fFAYQ1+m7aX0SbLg0Xr+6tvqndmNqc382/qBIbzXiGlUookrsjlPb6gLNurnTssCXjNM72J6lQ== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" hash.js "1.1.3" -"@ethersproject/signing-key@5.0.10", "@ethersproject/signing-key@^5.0.8": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.10.tgz#05e26e04f0aa5360dc78674d7331bacea8fea5c1" - integrity sha512-w5it3GbFOvN6e0mTd5gDNj+bwSe6L9jqqYjU+uaYS8/hAEp4qYLk5p8ZjbJJkNn7u1p0iwocp8X9oH/OdK8apA== +"@ethersproject/signing-key@5.0.11", "@ethersproject/signing-key@^5.0.8": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.11.tgz#19fc5c4597e18ad0a5efc6417ba5b74069fdd2af" + integrity sha512-Jfcru/BGwdkXhLxT+8WCZtFy7LL0TPFZw05FAb5asxB/MyVsEfNdNxGDtjVE9zXfmRSPe/EusXYY4K7wcygOyQ== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" elliptic "6.5.4" -"@ethersproject/solidity@5.0.9": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.9.tgz#49100fbe9f364ac56f7ff7c726f4f3d151901134" - integrity sha512-LIxSAYEQgLRXE3mRPCq39ou61kqP8fDrGqEeNcaNJS3aLbmAOS8MZp56uK++WsdI9hj8sNsFh78hrAa6zR9Jag== +"@ethersproject/solidity@5.0.10": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.10.tgz#128c9289761cf83d81ff62a1195d6079a924a86c" + integrity sha512-8OG3HLqynWXDA6mVIHuHfF/ojTTwBahON7hc9GAKCqglzXCkVA3OpyxOJXPzjHClRIAUUiU7r9oy9Z/nsjtT/g== dependencies: "@ethersproject/bignumber" "^5.0.13" "@ethersproject/bytes" "^5.0.9" @@ -366,19 +366,19 @@ "@ethersproject/sha2" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/strings@5.0.9", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.9.tgz#8e2eb2918b140231e1d1b883d77e43213a8ac280" - integrity sha512-ogxBpcUpdO524CYs841MoJHgHxEPUy0bJFDS4Ezg8My+WYVMfVAOlZSLss0Rurbeeam8CpUVDzM4zUn09SU66Q== +"@ethersproject/strings@5.0.10", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.10.tgz#ddce1e9724f4ac4f3f67e0cac0b48748e964bfdb" + integrity sha512-KAeoS1tZ9/5ECXiIZA6S6hywbD0so2VmuW+Wfyo5EDXeyZ6Na1nxTPhTnW7voQmjbeYJffCrOc0qLFJeylyg7w== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/constants" "^5.0.8" "@ethersproject/logger" "^5.0.8" -"@ethersproject/transactions@5.0.10", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.9": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.10.tgz#d50cafd80d27206336f80114bc0f18bc18687331" - integrity sha512-Tqpp+vKYQyQdJQQk4M73tDzO7ODf2D42/sJOcKlDAAbdSni13v6a+31hUdo02qYXhVYwIs+ZjHnO4zKv5BNk8w== +"@ethersproject/transactions@5.0.11", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.9": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.11.tgz#b31df5292f47937136a45885d6ee6112477c13df" + integrity sha512-ftsRvR9+gQp7L63F6+XmstvsZ4w8GtWvQB08e/zB+oB86Fnhq8+i/tkgpJplSHC8I/qgiCisva+M3u2GVhDFPA== dependencies: "@ethersproject/address" "^5.0.9" "@ethersproject/bignumber" "^5.0.13" @@ -390,19 +390,19 @@ "@ethersproject/rlp" "^5.0.7" "@ethersproject/signing-key" "^5.0.8" -"@ethersproject/units@5.0.10": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.10.tgz#9cca3b65cd0c92fab1bd33f2abd233546dd61987" - integrity sha512-eaiHi9ham5lbC7qpqxpae7OY/nHJUnRUnFFuEwi2VB5Nwe3Np468OAV+e+HR+jAK4fHXQE6PFBTxWGtnZuO37g== +"@ethersproject/units@5.0.11": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.11.tgz#f82f6e353ac0d6fa43b17337790f1f9aa72cb4c8" + integrity sha512-nOSPmcCWyB/dwoBRhhTtPGCsTbiXqmc7Q0Adwvafc432AC7hy3Fj3IFZtnSXsbtJ/GdHCIUIoA8gtvxSsFuBJg== dependencies: "@ethersproject/bignumber" "^5.0.13" "@ethersproject/constants" "^5.0.8" "@ethersproject/logger" "^5.0.8" -"@ethersproject/wallet@5.0.11": - version "5.0.11" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.11.tgz#9891936089d1b91e22ed59f850bc344b1544bf26" - integrity sha512-2Fg/DOvUltR7aZTOyWWlQhru+SKvq2UE3uEhXSyCFgMqDQNuc2nHXh1SHJtN65jsEbjVIppOe1Q7EQMvhmeeRw== +"@ethersproject/wallet@5.0.12": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.12.tgz#bfb96f95e066b4b1b4591c4615207b87afedda8b" + integrity sha512-rboJebGf47/KPZrKZQdYg9BAYuXbc/OwcUyML1K1f2jnJeo1ObWV11U1PAWTjTbhhSy6/Fg+34GO2yMb5Dt1Rw== dependencies: "@ethersproject/abstract-provider" "^5.0.8" "@ethersproject/abstract-signer" "^5.0.10" @@ -420,10 +420,10 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/web@5.0.13", "@ethersproject/web@^5.0.12": - version "5.0.13" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.13.tgz#5a92ac6d835d2ebce95b6b645a86668736e2f532" - integrity sha512-G3x/Ns7pQm21ALnWLbdBI5XkW/jrsbXXffI9hKNPHqf59mTxHYtlNiSwxdoTSwCef3Hn7uvGZpaSgTyxs7IufQ== +"@ethersproject/web@5.0.14", "@ethersproject/web@^5.0.12": + version "5.0.14" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.14.tgz#6e7bebdd9fb967cb25ee60f44d9218dc0803bac4" + integrity sha512-QpTgplslwZ0Sp9oKNLoRuS6TKxnkwfaEk3gr7zd7XLF8XBsYejsrQO/03fNfnMx/TAT/RR6WEw/mbOwpRSeVRA== dependencies: "@ethersproject/base64" "^5.0.7" "@ethersproject/bytes" "^5.0.9" @@ -431,10 +431,10 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/wordlists@5.0.9", "@ethersproject/wordlists@^5.0.8": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.9.tgz#f16cc0b317637c3ae9c689ebd7bc2cbbffadd013" - integrity sha512-Sn6MTjZkfbriod6GG6+p43W09HOXT4gwcDVNj0YoPYlo4Zq2Fk6b1CU9KUX3c6aI17PrgYb4qwZm5BMuORyqyQ== +"@ethersproject/wordlists@5.0.10", "@ethersproject/wordlists@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.10.tgz#177b9a0b4d72b9c4f304d08b36612d6c60e9b896" + integrity sha512-jWsEm1iJzpg9SCXnNfFz+tcp4Ofzv0TJb6mj+soCNcar9GcT0yGz62ZsHC3pLQWaF4LkCzGwRJHJTXKjHQfG1A== dependencies: "@ethersproject/bytes" "^5.0.9" "@ethersproject/hash" "^5.0.10" @@ -484,10 +484,10 @@ safe-buffer "^5.1.1" util.promisify "^1.0.0" -"@nomiclabs/hardhat-ethers@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.1.tgz#f86a6fa210dbe6270adffccc75e93ed60a856904" - integrity sha512-uTFHDhhvJ+UjfvvMeQxD3ZALuzuI3FXzTYT1Z5N3ebyZL5z4Ogwt55GB0R9tdKY0p5HhDhBjU/gsCjUEwIVoaw== +"@nomiclabs/hardhat-ethers@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" + integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== "@nomiclabs/hardhat-etherscan@^2.1.1": version "2.1.1" @@ -543,15 +543,15 @@ find-up "^4.1.0" fs-extra "^8.1.0" -"@openzeppelin/contracts-upgradeable@^3.3.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.0.tgz#7674c73c643a509f1b90c509e3e72fe9aae02aeb" - integrity sha512-7wBcbukDqWZt/B1zjb7zyeWq+AC7rx7nGln7/hPxHdKd8PAiiteXd51Cp2KmGP8qaY0/TXh/fQLsA082LWp8Zw== +"@openzeppelin/contracts-upgradeable@3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1.tgz#38dfdfa86fda0a088c6fcdebe6870cfaf897b471" + integrity sha512-wBGlUzEkOxcj/ghtcF2yKc8ZYh+PTUtm1mK38zoENulJ6aplij7eH8quo3lMugfzPJy+V6V5qI8QhdQmCn7hkQ== -"@openzeppelin/contracts@^3.3.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.0.tgz#9a1669ad5f9fdfb6e273bb5a4fed10cb4cc35eb0" - integrity sha512-qh+EiHWzfY/9CORr+eRUkeEUP1WiFUcq3974bLHwyYzLBUtK6HPaMkIUHi74S1rDTZ0sNz42DwPc5A4IJvN3rg== +"@openzeppelin/contracts@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.3.0.tgz#ffdb693c5c349fc33bba420248dd3ac0a2d7c408" + integrity sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw== "@openzeppelin/hardhat-upgrades@^1.6.0": version "1.6.0" @@ -665,15 +665,15 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@solidity-parser/parser@^0.11.0", "@solidity-parser/parser@^0.11.1": +"@solidity-parser/parser@^0.11.0": version "0.11.1" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== -"@solidity-parser/parser@^0.8.2": - version "0.8.2" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.8.2.tgz#a6a5e93ac8dca6884a99a532f133beba59b87b69" - integrity sha512-8LySx3qrNXPgB5JiULfG10O3V7QTxI/TLzSw5hFQhXWSkVxZBAv4rZQ0sYgLEbc8g3L2lmnujj1hKul38Eu5NQ== +"@solidity-parser/parser@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.0.tgz#18a0fb2a9d2484b23176f63b16093c64794fc323" + integrity sha512-DT3f/Aa4tQysZwUsuqBwvr8YRJzKkvPUKV/9o2/o5EVw3xqlbzmtx4O60lTUcZdCawL+N8bBLNUyOGpHjGlJVQ== "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -689,17 +689,15 @@ dependencies: source-map-support "^0.5.19" -"@truffle/blockchain-utils@^0.0.26": - version "0.0.26" - resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.26.tgz#f4ea794e0a18c74d73ea10e29a506c9ed0a503ee" - integrity sha512-M91NJkfapK1RqdzVwKSSenPEE2cHzAAFwC3aPhA8Y3DznRfzOcck4mDH6eY71sytVCrGaXGm/Wirn3drGSH+qQ== - dependencies: - source-map-support "^0.5.19" +"@truffle/blockchain-utils@^0.0.28": + version "0.0.28" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.28.tgz#6e2c7695c013bcce89af0f05de88210a25f7344a" + integrity sha512-Q3vtGzDAGI3q2OQV1rUX/HYGJ0PB4RKbrgnwSV3YLc01YOkv4baF5ZJbMxtgLDhJQap1llrN59u2BOAa+jVCsg== -"@truffle/codec@^0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.0.tgz#306425897f92b71cf2c9f665a45351e445b4c09b" - integrity sha512-jRqUPQkiiZmXtms34nI2WmEAA4pQnKUhdtJGCeqKUKu2igZ35ZMKoXkpg1stvahawn17XZxo+8DjbFlR0uHq7w== +"@truffle/codec@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.2.tgz#b59746c91124b56370011ac1e1ac9a489b35b3eb" + integrity sha512-m396zHfF5ee8zv0O0SctVt0/FxGPv/x9pTm24BHlDz540S6wiWwarBb20QF4sXfJ7lwe6ZfwtyQHExuszCENlg== dependencies: big.js "^5.2.2" bn.js "^5.1.3" @@ -710,7 +708,6 @@ lodash.partition "^4.6.0" lodash.sum "^4.0.2" semver "^7.3.4" - source-map-support "^0.5.19" utf8 "^3.0.0" web3-utils "1.2.9" @@ -742,19 +739,18 @@ debug "^4.3.1" "@truffle/contract@^4.0.35": - version "4.3.8" - resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.8.tgz#806c63d683be81e5fd09d54346b52d7bb3940b5b" - integrity sha512-pQjRVUQ9muV1zT/bzhCvyhQXLHQVZobhe/Dwt/sIoj5Alea0ueNPQTeLN6DmMWusNDSlY8fNM7mgXJ+izvdpxw== + version "4.3.12" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.12.tgz#9c1fed962fae17a8fe1895c6e80314a00dc62cce" + integrity sha512-gnOkZe28k/pwPkl4Ke9mj7UwLHDr6nq1ZitZOhukXvq5mf6Ll9hsHhGCwlzKnxdwKHHUpJ/1saqp6OjXOiJYEw== dependencies: - "@truffle/blockchain-utils" "^0.0.26" + "@truffle/blockchain-utils" "^0.0.28" "@truffle/contract-schema" "^3.3.4" - "@truffle/debug-utils" "^5.0.10" + "@truffle/debug-utils" "^5.0.12" "@truffle/error" "^0.0.12" - "@truffle/interface-adapter" "^0.4.19" + "@truffle/interface-adapter" "^0.4.20" bignumber.js "^7.2.1" ethereum-ens "^0.8.0" ethers "^4.0.32" - source-map-support "^0.5.19" web3 "1.2.9" web3-core-helpers "1.2.9" web3-core-promievent "1.2.9" @@ -773,12 +769,12 @@ highlight.js "^9.15.8" highlightjs-solidity "^1.0.18" -"@truffle/debug-utils@^5.0.10": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.10.tgz#8dad322d404870e313b3b92cdaa609ad6212e08e" - integrity sha512-N2jqeHDmVh0xIe/BL2Or0/AAGAHxd2dM55NOAJYFPdekbigQEpmobTZtfUfpZU1oTQe+1yHpXXonkkjf+AKY7w== +"@truffle/debug-utils@^5.0.12": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.12.tgz#82ff52cef545ca8c0274292fb97d473253df1570" + integrity sha512-NkdSLjAvyylZAOJeywYHzCWHkrAm0kVPVkZZUtsej0wAvzqqL6FWV/jy9wKtpAv5hYhCQBU9BKrPdfdTnE6dYA== dependencies: - "@truffle/codec" "^0.10.0" + "@truffle/codec" "^0.10.2" "@trufflesuite/chromafi" "^2.2.2" bn.js "^5.1.3" chalk "^2.4.2" @@ -796,23 +792,22 @@ resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.12.tgz#83e02e6ffe1d154fe274141d90038a91fd1e186d" integrity sha512-kZqqnPR9YDJG7KCDOcN1qH16Qs0oz1PzF0Y93AWdhXuL9S9HYo/RUUeqGKbPpRBEZldQUS8aa4EzfK08u5pu6g== -"@truffle/interface-adapter@^0.4.16", "@truffle/interface-adapter@^0.4.19": - version "0.4.19" - resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.19.tgz#19248ac88099f8df34f58a3d43a95ba3470dc89a" - integrity sha512-+Zz6Fr8+I2wYSS8RM3WBOMzf22QffMQTnlsYsRgRHzv3gYoRA9ZDLb84lFRfmWyw+IdXTo90tjRHEb5krC6uxg== +"@truffle/interface-adapter@^0.4.16", "@truffle/interface-adapter@^0.4.20": + version "0.4.20" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.20.tgz#cf3f176e6fe14321bf164d502bf8d05e50209eb5" + integrity sha512-GcdtXjU+Mhx/WuD3Af1thojHilhUAWtKfoNF09oEDyGSrS0QEWq9s9kOFjrfTFJrK+g0I6VXMrU2sIido96NBA== dependencies: bn.js "^5.1.3" ethers "^4.0.32" - source-map-support "^0.5.19" web3 "1.2.9" "@truffle/provider@^0.2.24": - version "0.2.26" - resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.26.tgz#88e31b79973c2427c4a17d9a59411e6fbc810190" - integrity sha512-YKPmhB9S9AQkT2ePGtadwjDduxU23DXXy+5zyM5fevw5GCbXSnf+jG6rICXjPkVFjuKBlXuq5JbuERZn43522Q== + version "0.2.27" + resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.27.tgz#8d06f94df6e6ea632ccd4518c419f6d3c0b05531" + integrity sha512-PwFcrH++FslHQMZEiID6t6CFTVavJHj4EVszmMS3E4+P9HIcpZTh/hHACqXhvlBxv94J9yM6SFmcSSokn8GC7A== dependencies: "@truffle/error" "^0.0.12" - "@truffle/interface-adapter" "^0.4.19" + "@truffle/interface-adapter" "^0.4.20" web3 "1.2.9" "@trufflesuite/chromafi@^2.2.1", "@trufflesuite/chromafi@^2.2.2": @@ -849,6 +844,13 @@ dependencies: "@types/node" "*" +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + "@types/chai@^4.2.0": version "4.2.15" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.15.tgz#b7a6d263c2cecf44b6de9a051cf496249b154553" @@ -882,24 +884,24 @@ integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== "@types/minimatch@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" + integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== "@types/node@*": - version "14.14.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" - integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== + version "14.14.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" + integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== "@types/node@^10.0.3", "@types/node@^10.12.18": - version "10.17.54" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.54.tgz#a737488631aca3ec7bd9f6229d77f1079e444793" - integrity sha512-c8Lm7+hXdSPmWH4B9z/P/xIXhFK3mCQin4yCYMd2p1qpMG5AfgyJuYZ+3q2dT7qLiMMMGMd5dnkFpdqJARlvtQ== + version "10.17.56" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.56.tgz#010c9e047c3ff09ddcd11cbb6cf5912725cdc2b3" + integrity sha512-LuAa6t1t0Bfw4CuSR0UITsm1hP17YL+u82kfHGrHUWdhlBtH7sa7jGY5z7glGaIj/WDYDkRtgGd+KCjCzxBW1w== "@types/node@^12.12.6", "@types/node@^12.6.1": - version "12.20.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.4.tgz#73687043dd00fcb6962c60fbf499553a24d6bdf2" - integrity sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ== + version "12.20.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.7.tgz#1cb61fd0c85cb87e728c43107b5fd82b69bc9ef8" + integrity sha512-gWL8VUkg8VRaCAUgG9WmhefMqHmMblxe2rVpMF86nZY/+ZysU+BkAp+3cz03AixWDSSz0ks5WX59yAhv/cDwFA== "@types/node@^8.0.0": version "8.10.66" @@ -919,9 +921,9 @@ "@types/node" "*" "@types/qs@^6.2.31": - version "6.9.5" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" - integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + version "6.9.6" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" + integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== "@types/secp256k1@^4.0.1": version "4.0.1" @@ -1032,9 +1034,9 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: uri-js "^4.2.2" ajv@^7.0.2: - version "7.1.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84" - integrity sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ== + version "7.2.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.4.tgz#8e239d4d56cf884bccca8cca362f508446dc160f" + integrity sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -1067,11 +1069,11 @@ ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: - type-fest "^0.11.0" + type-fest "^0.21.3" ansi-mark@^1.0.0: version "1.0.4" @@ -1597,9 +1599,9 @@ cbor@^5.0.2, cbor@^5.1.0: nofilter "^1.0.4" cbor@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.3.tgz#216d292f2aedd1bb61414a8f949b63e4de11b33b" - integrity sha512-Io+lJytjYBJKgJqZQUz2bFaMPj+HtlsnT9kHSUiIJFqzWa05lm5/ycQ+NiZWpks3DR2Fz7j7axiTGeT57w/syg== + version "7.0.4" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.4.tgz#8a3ef39f07ac3fdb69dda461b87db7113233481b" + integrity sha512-9hBTn31l7+9qteBso7+HPp2R5ytqFRBd98fHK4ZTpvrba8V7CuoOsEL0S6vf7+11gubMTd3RW97lOgMTl5SNfg== dependencies: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" @@ -1609,16 +1611,16 @@ chai-bn@^0.2.1: resolved "https://registry.yarnpkg.com/chai-bn/-/chai-bn-0.2.1.tgz#1dad95e24c3afcd8139ab0262e9bbefff8a30ab7" integrity sha512-01jt2gSXAw7UYFPT5K8d7HYjdXj2vyeIuE+0T/34FWzlNcVbs1JkPxRu7rYMfQnJhrHT8Nr6qjSf5ZwwLU2EYg== -chai@^4.2.0, chai@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.0.tgz#5523a5faf7f819c8a92480d70a8cccbadacfc25f" - integrity sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA== +chai@^4.2.0, chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" - pathval "^1.1.0" + pathval "^1.1.1" type-detect "^4.0.5" chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: @@ -1788,9 +1790,9 @@ cli-table3@^0.6.0: colors "^1.1.2" cli-table@^0.3.1: - version "0.3.5" - resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.5.tgz#643508c1d6b6e7b02c82c18afd5fcc8b6dab3ca6" - integrity sha512-7uo2+RMNQUZ13M199udxqwk1qxTOS53EUak4gmu/aioUpdH5RvBz0JkJslcWz6ABKedZNqXXzikMZgHh+qF16A== + version "0.3.6" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc" + integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ== dependencies: colors "1.0.3" @@ -1951,9 +1953,9 @@ cookiejar@^2.1.1: integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== core-js-pure@^3.0.1: - version "3.9.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.0.tgz#326cc74e1fef8b7443a6a793ddb0adfcd81f9efb" - integrity sha512-3pEcmMZC9Cq0D4ZBh3pe2HLtqxpGNJBLXF/kZ2YzK17RbKp94w0HFbdbSx8H8kAlZG5k76hvLrkPm57Uyef+kg== + version "3.9.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.1.tgz#677b322267172bd490e4464696f790cbc355bec5" + integrity sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2286,9 +2288,9 @@ domhandler@^4.0.0: domelementtype "^2.1.0" domutils@^2.4.3, domutils@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" - integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039" + integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg== dependencies: dom-serializer "^1.0.1" domelementtype "^2.0.1" @@ -2398,9 +2400,9 @@ entities@~2.1.0: integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== env-paths@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" - integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== errno@~0.1.1: version "0.1.8" @@ -2417,24 +2419,26 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: - version "1.18.0-next.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" - integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== + version "1.18.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" + integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" - get-intrinsic "^1.0.2" + get-intrinsic "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" + has-symbols "^1.0.2" + is-callable "^1.2.3" is-negative-zero "^2.0.1" - is-regex "^1.1.1" + is-regex "^1.1.2" + is-string "^1.0.5" object-inspect "^1.9.0" object-keys "^1.1.1" object.assign "^4.1.2" - string.prototype.trimend "^1.0.3" - string.prototype.trimstart "^1.0.3" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -2587,10 +2591,10 @@ eslint@^5.6.0: table "^5.2.3" text-table "^0.2.0" -eslint@^7.20.0: - version "7.21.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" - integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== +eslint@^7.23.0: + version "7.23.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325" + integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -2609,7 +2613,7 @@ eslint@^7.20.0: file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -2617,7 +2621,7 @@ eslint@^7.20.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.21" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -2706,12 +2710,12 @@ eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: js-sha3 "^0.5.7" eth-gas-reporter@^0.2.20: - version "0.2.21" - resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.21.tgz#8cb879a1085bf180d38ac46bb5e4cd99c774c99c" - integrity sha512-lSl9wjxPCxhDcvwvF1UwKHAUtOpjm9Lr/Can3GuxB18mACtkIlrHe3PDwt0kpYvvFvHjxiG5dO9cjovaIa+8gQ== + version "0.2.22" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.22.tgz#bbe91f5d7b22433d26f099eeb5b20118ced0e575" + integrity sha512-L1FlC792aTf3j/j+gGzSNlGrXKSxNPXQNk6TnV5NNZ2w3jnQCRyJjDl0zUo25Cq2t90IS5vGdbkwqFQK7Ce+kw== dependencies: "@ethersproject/abi" "^5.0.0-beta.146" - "@solidity-parser/parser" "^0.11.1" + "@solidity-parser/parser" "^0.12.0" cli-table3 "^0.5.0" colors "^1.1.2" ethereumjs-util "6.2.0" @@ -2922,12 +2926,12 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.2.0: rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.0.8: - version "7.0.8" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.8.tgz#5258762b7b17e3d828e41834948363ff0a703ffd" - integrity sha512-JJt7tDpCAmDPw/sGoFYeq0guOVqT3pTE9xlEbBmc/nlCij3JRCoS2c96SQ6kXVHOT3xWUNLDm5QCJLQaUnVAtQ== +ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.0.9: + version "7.0.9" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.9.tgz#2038baeb30f370a3e576ec175bd70bbbb6807d42" + integrity sha512-cRqvYYKJoitq6vMKMf8pXeVwvTrX+dRD0JwHaYqm8jvogK14tqIoCWH/KUHcRwnVxVXEYF/o6pup5jRG4V0xzg== dependencies: - "@types/bn.js" "^4.11.3" + "@types/bn.js" "^5.1.0" bn.js "^5.1.2" create-hash "^1.1.2" ethereum-cryptography "^0.1.3" @@ -2949,41 +2953,41 @@ ethers@^4.0.0-beta.1, ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.31: - version "5.0.31" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.31.tgz#60e3b1425864fe5d2babc147ede01be8382a7d2a" - integrity sha512-zpq0YbNFLFn+t+ibS8UkVWFeK5w6rVMSvbSHrHAQslfazovLnQ/mc2gdN5+6P45/k8fPgHrfHrYvJ4XvyK/S1A== - dependencies: - "@ethersproject/abi" "5.0.12" - "@ethersproject/abstract-provider" "5.0.9" - "@ethersproject/abstract-signer" "5.0.13" - "@ethersproject/address" "5.0.10" - "@ethersproject/base64" "5.0.8" - "@ethersproject/basex" "5.0.8" - "@ethersproject/bignumber" "5.0.14" - "@ethersproject/bytes" "5.0.10" - "@ethersproject/constants" "5.0.9" - "@ethersproject/contracts" "5.0.11" - "@ethersproject/hash" "5.0.11" - "@ethersproject/hdnode" "5.0.9" - "@ethersproject/json-wallets" "5.0.11" - "@ethersproject/keccak256" "5.0.8" - "@ethersproject/logger" "5.0.9" - "@ethersproject/networks" "5.0.8" - "@ethersproject/pbkdf2" "5.0.8" - "@ethersproject/properties" "5.0.8" - "@ethersproject/providers" "5.0.23" - "@ethersproject/random" "5.0.8" - "@ethersproject/rlp" "5.0.8" - "@ethersproject/sha2" "5.0.8" - "@ethersproject/signing-key" "5.0.10" - "@ethersproject/solidity" "5.0.9" - "@ethersproject/strings" "5.0.9" - "@ethersproject/transactions" "5.0.10" - "@ethersproject/units" "5.0.10" - "@ethersproject/wallet" "5.0.11" - "@ethersproject/web" "5.0.13" - "@ethersproject/wordlists" "5.0.9" +ethers@^5.0.32: + version "5.0.32" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.32.tgz#f009970be31d96a589bf0ce597a39c10c7e297a6" + integrity sha512-rORfGWR0HsA4pjKMMcWZorw12DHsXqfIAuPVHJsXt+vI24jvXcVqx+rLsSvgOoLdaCMdxiN5qlIq2+4axKG31g== + dependencies: + "@ethersproject/abi" "5.0.13" + "@ethersproject/abstract-provider" "5.0.10" + "@ethersproject/abstract-signer" "5.0.14" + "@ethersproject/address" "5.0.11" + "@ethersproject/base64" "5.0.9" + "@ethersproject/basex" "5.0.9" + "@ethersproject/bignumber" "5.0.15" + "@ethersproject/bytes" "5.0.11" + "@ethersproject/constants" "5.0.10" + "@ethersproject/contracts" "5.0.12" + "@ethersproject/hash" "5.0.12" + "@ethersproject/hdnode" "5.0.10" + "@ethersproject/json-wallets" "5.0.12" + "@ethersproject/keccak256" "5.0.9" + "@ethersproject/logger" "5.0.10" + "@ethersproject/networks" "5.0.9" + "@ethersproject/pbkdf2" "5.0.9" + "@ethersproject/properties" "5.0.9" + "@ethersproject/providers" "5.0.24" + "@ethersproject/random" "5.0.9" + "@ethersproject/rlp" "5.0.9" + "@ethersproject/sha2" "5.0.9" + "@ethersproject/signing-key" "5.0.11" + "@ethersproject/solidity" "5.0.10" + "@ethersproject/strings" "5.0.10" + "@ethersproject/transactions" "5.0.11" + "@ethersproject/units" "5.0.11" + "@ethersproject/wallet" "5.0.12" + "@ethersproject/web" "5.0.14" + "@ethersproject/wordlists" "5.0.10" ethjs-abi@^0.2.1: version "0.2.1" @@ -3452,7 +3456,7 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-intrinsic@^1.0.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== @@ -3506,9 +3510,9 @@ ghost-testrpc@^0.0.2: node-emoji "^1.10.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -3583,6 +3587,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" + integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + dependencies: + type-fest "^0.20.2" + globby@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -3669,15 +3680,15 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -hardhat-abi-exporter@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.0.8.tgz#03ec54a5f51256c4bb447ce016c3a3041660071a" - integrity sha512-8Q2SH//RSzuzjHlnfaQkpfbZXVHkWKM1dWIyVwGVzREBdYfBW5SC/3safvU7KGy5gmLH/P3fgq4uuqNmGvDUrw== +hardhat-abi-exporter@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.2.0.tgz#fc5b4c4cb7441fe2b65a1fd2f2e2c9dede624f60" + integrity sha512-rr3Sm8NnGTwfwnM65PQrs1x5z7A4P3Xs2MNMdnCWheQrsD/uPl0RnN+r3vo2GbLxauZ0xHbvWMfXvrgrd4WCEw== -hardhat-contract-sizer@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.0.2.tgz#735e00c4776188683886226b9b37dfe75cbd16fa" - integrity sha512-6vDj3OoqPvzuEnURY2lN6veFXH8uIBRgnaIJoN7cpn7ENuciIR3qotNgLtbC6BNp4y0Cn/8FeMfKS6MdAvIdgQ== +hardhat-contract-sizer@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.0.3.tgz#604455fd803865f81c29f60364e863eaa19395a7" + integrity sha512-iaixOzWxwOSIIE76cl2uk4m9VXI1hKU3bFt+gl7jDhyb2/JB2xOp5wECkfWqAoc4V5lD4JtjldZlpSTbzX+nPQ== dependencies: cli-table3 "^0.6.0" colors "^1.4.0" @@ -3690,10 +3701,10 @@ hardhat-gas-reporter@^1.0.4: eth-gas-reporter "^0.2.20" sha1 "^1.1.1" -hardhat@^2.0.11: - version "2.0.11" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.0.11.tgz#f7bc9d38045ecda58665dcf1ffae9ff940712f06" - integrity sha512-K3cyXV/F0reT0Lu7fHHBAgVOVenUaYa6uCNvzYbFnjKH+s8O4CEdrsFQ/yIbmLgMpC2EQeuBSwb+8ZhWVj8AGQ== +hardhat@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.1.2.tgz#a2128b71b0fb216ffc978c85a2030835b4e306ea" + integrity sha512-42iOheDsDl6Gr7sBfpA0S+bQUIcXSDEUrrqmnFEcBHx9qBoQad3s212y2ODmmkdLt+PqqTM+Mq8N3bZDTdjoLg== dependencies: "@nomiclabs/ethereumjs-vm" "4.2.2" "@sentry/node" "^5.18.1" @@ -3725,6 +3736,7 @@ hardhat@^2.0.11: io-ts "1.10.4" lodash "^4.17.11" merkle-patricia-tree "3.0.0" + mnemonist "^0.38.0" mocha "^7.1.2" node-fetch "^2.6.0" qs "^6.7.0" @@ -3740,6 +3752,11 @@ hardhat@^2.0.11: uuid "^3.3.2" ws "^7.2.1" +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" @@ -3765,7 +3782,7 @@ has-symbol-support-x@^1.4.1: resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== -has-symbols@^1.0.0, has-symbols@^1.0.1: +has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== @@ -3815,9 +3832,9 @@ he@1.2.0, he@^1.1.1: integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== highlight.js@^10.4.0, highlight.js@^10.4.1: - version "10.6.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6" - integrity sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ== + version "10.7.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.1.tgz#a8ec4152db24ea630c90927d6cae2a45f8ecb955" + integrity sha512-S6G97tHGqJ/U8DsXcEdnACbirtbx58Bx9CzIVeYli8OuswCfYI/LsXH2EiGcoGio1KAC3x4mmUwulOllJ2ZyRA== highlight.js@^9.15.8: version "9.18.5" @@ -3839,9 +3856,9 @@ hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: minimalistic-crypto-utils "^1.0.1" htmlparser2@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01" - integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw== + version "6.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446" + integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w== dependencies: domelementtype "^2.0.1" domhandler "^4.0.0" @@ -3919,10 +3936,10 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== -husky@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/husky/-/husky-5.1.1.tgz#8678953fd8deb86f387cbf1ad50bb2f7f96e83f2" - integrity sha512-80LZ736V0Nr4/st0c2COYaMbEQhHNmAbLMN8J/kLk7/mo0QdUkUGNDjv/7jVkhug377Wh8wfbWyaVXEJ/h2B/Q== +husky@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-5.2.0.tgz#fc5e1c2300d34855d47de4753607d00943fc0802" + integrity sha512-AM8T/auHXRBxlrfPVLKP6jt49GCM2Zz47m8G3FOMsLmTv8Dj/fKVWE0Rh2d4Qrvmy131xEsdQnb3OXRib67PGg== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" @@ -4054,9 +4071,9 @@ io-ts@1.10.4: fp-ts "^1.0.0" io-ts@^2.2.9: - version "2.2.15" - resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.2.15.tgz#0b0b19a6f8a64f4a524ad5810d56432789428128" - integrity sha512-ww2ZPrErx5pjCCI/tWRwjlEIDEndnN9kBIxAylXj+WNIH4ZVgaUqFuabGouehkRuvrmvzO5OnZmLf+o50h4izQ== + version "2.2.16" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.2.16.tgz#597dffa03db1913fc318c9c6df6931cb4ed808b2" + integrity sha512-y5TTSa6VP6le0hhmIyN0dqEXkrZeJLeC5KApJq6VLci3UEKF80lZ+KuoUs02RhBxNWlrqSNxzfI7otLX1Euv8Q== ipaddr.js@1.9.1: version "1.9.1" @@ -4075,6 +4092,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -4082,12 +4104,19 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" + integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + dependencies: + call-bind "^1.0.0" + is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.2: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== @@ -4151,6 +4180,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -4171,7 +4205,7 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-regex@^1.1.1: +is-regex@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== @@ -4199,7 +4233,12 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-symbol@^1.0.2: +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== @@ -4222,6 +4261,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4574,9 +4618,9 @@ lint-staged@10.5.4: stringify-object "^3.3.0" listr2@^3.2.2: - version "3.3.4" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.3.4.tgz#bca480e784877330b9d96d6cdc613ad243332e20" - integrity sha512-b0lhLAvXSr63AtPF9Dgn6tyxm8Kiz6JXpVGM0uZJdnDcZp02jt7FehgAnMfA9R7riQimOKjQgLknBTdz2nmXwQ== + version "3.4.3" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.4.3.tgz#543bcf849d5ffc70602708b69d2daac73f751699" + integrity sha512-wZmkzNiuinOfwrGqAwTCcPw6aKQGTAMGXwG5xeU1WpDjJNeBA35jGBeWxR3OF+R6Yl5Y3dRG+3vE8t6PDcSNHA== dependencies: chalk "^4.1.0" cli-truncate "^2.1.0" @@ -4646,7 +4690,7 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4659,11 +4703,12 @@ log-symbols@3.0.0: chalk "^2.4.2" log-symbols@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^4.0.0" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" log-update@^4.0.0: version "4.0.0" @@ -4958,6 +5003,13 @@ mkdirp@0.5.5, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1: dependencies: minimist "^1.2.5" +mnemonist@^0.38.0: + version "0.38.3" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" + integrity sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw== + dependencies: + obliterator "^1.6.1" + mocha@^7.1.1, mocha@^7.1.2: version "7.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" @@ -5235,6 +5287,11 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1 define-properties "^1.1.3" es-abstract "^1.18.0-next.2" +obliterator@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" + integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== + oboe@2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" @@ -5512,7 +5569,7 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathval@^1.1.0: +pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== @@ -5672,9 +5729,11 @@ qs@6.7.0: integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.4.0, qs@^6.7.0: - version "6.9.6" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" - integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + version "6.10.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== + dependencies: + side-channel "^1.0.4" qs@~6.5.2: version "6.5.2" @@ -5691,9 +5750,9 @@ query-string@^5.0.1: strict-uri-encode "^1.0.0" queue-microtask@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" @@ -6042,9 +6101,9 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sc-istanbul@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.5.tgz#1896066484d55336cf2cdbcc7884dc79da50dc76" - integrity sha512-7wR5EZFLsC4w0wSm9BUuCgW+OGKAU7PNlW5L0qwVPbh+Q1sfVn2fyzfMXYCm6rkNA5ipaCOt94nApcguQwF5Gg== + version "0.4.6" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" + integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== dependencies: abbrev "1.0.x" async "1.x" @@ -6115,9 +6174,9 @@ semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.2.1, semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" @@ -6235,6 +6294,15 @@ shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -6301,12 +6369,12 @@ solc@0.7.3: semver "^5.5.0" tmp "0.0.33" -solhint@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.2.tgz#ebd7270bb50fd378b427d7a6fc9f2a7fd00216c0" - integrity sha512-8tHCkIAk1axLLG6Qu2WIH3GgNABonj9eAWejJbov3o3ujkZQRNHeHU1cC4/Dmjsh3Om7UzFFeADUHu2i7ZJeiw== +solhint@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.4.tgz#81770c60eeb027e6e447cb91ed599baf5e888e09" + integrity sha512-AEyjshF/PC6kox1c1l79Pji+DK9WVuk5u2WEh6bBKt188gWa63NBOAgYg0fBRr5CTUmsuGc1sGH7dgUVs83mKw== dependencies: - "@solidity-parser/parser" "^0.8.2" + "@solidity-parser/parser" "^0.12.0" ajv "^6.6.1" antlr4 "4.7.1" ast-parents "0.0.1" @@ -6324,16 +6392,16 @@ solhint@^3.3.2: prettier "^1.14.3" solidity-ast@^0.4.15: - version "0.4.17" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.17.tgz#e857b4f64466f3eb94da78fe961c0d256c84b228" - integrity sha512-5jkkabmjPdy9W9c2DMQBlKobVBz7KDnipxn+0zW191uD6BML3w7dQ+ihUvwA9XOm9ILDECrb5Y8z2vu47STqCg== + version "0.4.19" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.19.tgz#3c6c27da922f2afd4a10522c8ecfc38f55095bdd" + integrity sha512-Mzg2lLr3VMN8V6ukmuLRB0cf1wuvNw78lR8dIhZOxlHSb4Nsy1ABUXJ31oA9IrLJIlI2v+C2WajVu0pFfVb2qA== -solidity-coverage@^0.7.15: - version "0.7.15" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.15.tgz#c5974e92b665d5be6bee11c24bbf7f6ff7529fa2" - integrity sha512-MVcpEUyMjf+ITew3BS7fI/NRxpYJUg9wPrSLS7y/F7BwXpYVRgAK4WsjbMzVkRdmezta1Kon0XqYHZMLgEt9XA== +solidity-coverage@^0.7.16: + version "0.7.16" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.16.tgz#c8c8c46baa361e2817bbf275116ddd2ec90a55fb" + integrity sha512-ttBOStywE6ZOTJmmABSg4b8pwwZfYKG8zxu40Nz+sRF5bQX7JULXWj/XbX0KXps3Fsp8CJXg8P29rH3W54ipxw== dependencies: - "@solidity-parser/parser" "^0.11.0" + "@solidity-parser/parser" "^0.12.0" "@truffle/provider" "^0.2.24" chalk "^2.4.2" death "^1.1.0" @@ -6446,15 +6514,15 @@ string-width@^3.0.0, string-width@^3.1.0: strip-ansi "^5.1.0" string-width@^4.1.0, string-width@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.1.tgz#1933ce1f470973d224368009bd1316cad81d5f4f" - integrity sha512-LL0OLyN6AnfV9xqGQpDBwedT2Rt63737LxvsRxbcwpa2aIeynBApG2Sm//F3TaLHIR1aJBN52DWklc06b94o5Q== + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.3: +string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== @@ -6462,7 +6530,7 @@ string.prototype.trimend@^1.0.3: call-bind "^1.0.2" define-properties "^1.1.3" -string.prototype.trimstart@^1.0.3: +string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== @@ -6794,10 +6862,15 @@ type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.7.1: version "0.7.1" @@ -6823,9 +6896,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.3.0.tgz#ada7c045f07ead08abf9e2edd29be1a0c0661132" - integrity sha512-rgPIqOdfK/4J9FhiVrZ3cveAjRRo5rsQBAIhnylX874y1DX/kEKSVdLsnuHB6l1KTjHyU01VjiMBHgU2adejyg== + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -6840,24 +6913,34 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= uglify-js@^3.1.4: - version "3.12.8" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.8.tgz#a82e6e53c9be14f7382de3d068ef1e26e7d4aaf8" - integrity sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w== + version "3.13.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.2.tgz#fe10319861bccc8682bfe2e8151fbdd8aa921c44" + integrity sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw== ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== +unbox-primitive@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + underscore@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== underscore@^1.8.3: - version "1.12.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.0.tgz#4814940551fc80587cef7840d1ebb0f16453be97" - integrity sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ== + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== universalify@^0.1.0: version "0.1.2" @@ -6966,9 +7049,9 @@ uuid@^3.3.2: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@^2.0.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" - integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== varint@^5.0.0: version "5.0.2" @@ -7480,6 +7563,17 @@ websocket@^1.0.31, websocket@^1.0.32: utf-8-validate "^5.0.2" yaeti "^0.0.6" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -7583,9 +7677,9 @@ ws@^3.0.0: ultron "~1.1.0" ws@^7.2.1: - version "7.4.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" - integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== + version "7.4.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" + integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== xhr-request-promise@^0.1.2: version "0.1.3" @@ -7662,9 +7756,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: version "13.1.2" From daad52ecb56324c1c16b2be39d2d7ef5dfd5924e Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:52:32 +0300 Subject: [PATCH 06/25] Add StakeWiseToken abi --- abi/StakeWiseToken.json | 744 ++++++++++++++++++++++++++++++++++++++++ hardhat.config.js | 1 + 2 files changed, 745 insertions(+) create mode 100644 abi/StakeWiseToken.json diff --git a/abi/StakeWiseToken.json b/abi/StakeWiseToken.json new file mode 100644 index 00000000..3265c651 --- /dev/null +++ b/abi/StakeWiseToken.json @@ -0,0 +1,744 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAUSER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addPauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isAdmin", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isPauser", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removePauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/hardhat.config.js b/hardhat.config.js index adf865d1..15b9c22c 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -136,6 +136,7 @@ module.exports = { 'RewardEthToken', 'Solos', 'StakedEthToken', + 'StakeWiseToken', 'Validators', ], clear: true, From e5bad843e3038c88bda33be03a374e89c0031879 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:41:36 +0300 Subject: [PATCH 07/25] Implement vesting escrow and factory --- contracts/interfaces/IVestingEscrow.sol | 106 +++++++ .../interfaces/IVestingEscrowFactory.sol | 66 +++++ contracts/vestings/VestingEscrow.sol | 115 ++++++++ contracts/vestings/VestingEscrowFactory.sol | 92 ++++++ deployments/vestings.js | 29 ++ test/vestings/VestingEscrow.test.js | 265 ++++++++++++++++++ test/vestings/VestingEscrowFactory.test.js | 199 +++++++++++++ 7 files changed, 872 insertions(+) create mode 100644 contracts/interfaces/IVestingEscrow.sol create mode 100644 contracts/interfaces/IVestingEscrowFactory.sol create mode 100644 contracts/vestings/VestingEscrow.sol create mode 100644 contracts/vestings/VestingEscrowFactory.sol create mode 100644 deployments/vestings.js create mode 100644 test/vestings/VestingEscrow.test.js create mode 100644 test/vestings/VestingEscrowFactory.test.js diff --git a/contracts/interfaces/IVestingEscrow.sol b/contracts/interfaces/IVestingEscrow.sol new file mode 100644 index 00000000..cf10bb60 --- /dev/null +++ b/contracts/interfaces/IVestingEscrow.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +/** + * @dev Interface of the VestingEscrow contract. + */ +interface IVestingEscrow { + /** + * @dev Event for tracking escrow stoppage. + * @param sender - address of the transaction sender. + * @param beneficiary - address of the beneficiary where all the vested tokens were transferred. + * @param amount - amount of vested tokens pulled. + */ + event Stopped(address indexed sender, address indexed beneficiary, uint256 amount); + + /** + * @dev Event for tracking escrow claims. + * @param sender - address of the transaction sender. + * @param beneficiary - address of the beneficiary where the tokens were transferred. + * @param amount - amount of unvested tokens claimed. + */ + event Claimed(address indexed sender, address indexed beneficiary, uint256 amount); + + /** + * @dev Constructor for initializing the VestingEscrow contract. + * @param _admin - address of the contract admin. + * @param _token - address of the token. + * @param _recipient - address of the recipient of the tokens. + * @param _totalAmount - amount of tokens to vest. + * @param _startTime - start timestamp of the vesting in seconds. + * @param _endTime - end timestamp of the vesting in seconds. + * @param _cliffLength - cliff length in seconds. + */ + function initialize( + address _admin, + address _token, + address _recipient, + uint256 _totalAmount, + uint256 _startTime, + uint256 _endTime, + uint256 _cliffLength + ) external; + + /** + * @dev Function for fetching the total vested tokens amount. + */ + function vestedAmount() external view returns (uint256); + + /** + * @dev Function for fetching the total unclaimed tokens amount. + */ + function unclaimedAmount() external view returns (uint256); + + /** + * @dev Function for fetching the token contract address. + */ + function token() external view returns (IERC20Upgradeable); + + /** + * @dev Function for fetching the recipient address. + */ + function recipient() external view returns (address); + + /** + * @dev Function for fetching the total vested amount. + */ + function totalAmount() external view returns (uint256); + + /** + * @dev Function for fetching the total claimed amount. + */ + function claimedAmount() external view returns (uint256); + + /** + * @dev Function for fetching vesting start time. + */ + function startTime() external view returns (uint256); + + /** + * @dev Function for fetching vesting end time. + */ + function endTime() external view returns (uint256); + + /** + * @dev Function for fetching vesting cliff length. + */ + function cliffLength() external view returns (uint256); + + /** + * @dev Function for stopping the vesting contract. + * Can be called only by admin. The unvested tokens will be transferred to the beneficiary address. + * @param beneficiary - address of the unvested tokens recipient. + */ + function stop(address beneficiary) external; + + /** + * @dev Function for claiming the vested tokens. + * Can be called only by the tokens recipient. The amount claimed must be vested by the time of calling. + * @param beneficiary - address of the tokens recipient. + * @param amount - amount of tokens to claim. + */ + function claim(address beneficiary, uint256 amount) external; +} diff --git a/contracts/interfaces/IVestingEscrowFactory.sol b/contracts/interfaces/IVestingEscrowFactory.sol new file mode 100644 index 00000000..5bbbc2ba --- /dev/null +++ b/contracts/interfaces/IVestingEscrowFactory.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +/** + * @dev Interface of the VestingEscrowFactory contract. + */ +interface IVestingEscrowFactory { + /** + * @dev Event for tracking created vesting escrows. + * @param admin - address of the contract admin. + * @param token - address of the token. + * @param recipient - address of the recipient of the tokens. + * @param escrow - address of the escrow contract. + * @param totalAmount - amount of tokens to vest. + * @param startTime - start timestamp of the vesting in seconds. + * @param endTime - end timestamp of the vesting in seconds. + * @param cliffLength - cliff length in seconds. + */ + event VestingEscrowCreated( + address indexed admin, + address indexed token, + address indexed recipient, + address escrow, + uint256 totalAmount, + uint256 startTime, + uint256 endTime, + uint256 cliffLength + ); + + /** + * @dev Constructor for initializing the VestingEscrowFactory contract. + * @param _admin - address of the contract admin. + * @param _escrowImplementation - address of the VestingEscrow implementation contract. + */ + function initialize(address _admin, address _escrowImplementation) external; + + /** + * @dev Function for fetching escrow implementation contract. + */ + function escrowImplementation() external view returns (address); + + /** + * @dev Function for checking the total locked amount for all the escrows of the user. + * @param account - account to check the balance for. + */ + function balanceOf(address account) external view returns (uint256 total); + + /** + * @dev Function for deploying new escrow contract. + * @param token - address of the token contract. + * @param recipient - address of the recipient of the tokens. + * @param amount - amount of tokens to vest. + * @param vestingStart - start timestamp of the vesting in seconds. + * @param vestingDuration - vesting duration in seconds. + * @param cliffLength - cliff length in seconds. + */ + function deployEscrow( + address token, + address recipient, + uint256 amount, + uint256 vestingStart, + uint256 vestingDuration, + uint256 cliffLength + ) external returns (address escrow); +} diff --git a/contracts/vestings/VestingEscrow.sol b/contracts/vestings/VestingEscrow.sol new file mode 100644 index 00000000..8e4d4865 --- /dev/null +++ b/contracts/vestings/VestingEscrow.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; +import "../presets/OwnablePausableUpgradeable.sol"; +import "../interfaces/IVestingEscrow.sol"; + + +/** + * @title VestingEscrow + * + * @dev VestingEscrow contract vests tokens over a period of time with optional cliff. + * Admin user can stop the vesting and withdraw locked tokens. + */ +contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { + using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeMathUpgradeable for uint256; + + // @dev Address of the token contract. + IERC20Upgradeable public override token; + + // @dev Address of the recipient. + address public override recipient; + + // @dev Total amount vested. + uint256 public override totalAmount; + + // @dev Total amount claimed. + uint256 public override claimedAmount; + + // @dev Vesting start time. + uint256 public override startTime; + + // @dev Vesting end time. + uint256 public override endTime; + + // @dev Cliff length. + uint256 public override cliffLength; + + /** + * @dev See {IVestingEscrow-initialize}. + */ + function initialize( + address _admin, + address _token, + address _recipient, + uint256 _totalAmount, + uint256 _startTime, + uint256 _endTime, + uint256 _cliffLength + ) + external override initializer + { + __OwnablePausableUpgradeable_init(_admin); + token = IERC20Upgradeable(_token); + recipient = _recipient; + totalAmount = _totalAmount; + startTime = _startTime; + endTime = _endTime; + cliffLength = _cliffLength; + token.safeTransferFrom(msg.sender, address(this), _totalAmount); + } + + /** + * @dev See {IVestingEscrow-vestedAmount}. + */ + function vestedAmount() public view override returns (uint256) { + uint256 _startTime = startTime; + uint256 _endTime = endTime; + if (block.timestamp < _startTime.add(cliffLength)) return 0; + else if (_endTime <= block.timestamp || _endTime <= _startTime) return totalAmount; + return totalAmount.mul(block.timestamp.sub(_startTime)).div(_endTime.sub(_startTime)); + } + + /** + * @dev See {IVestingEscrow-unclaimedAmount}. + */ + function unclaimedAmount() public view override returns (uint256) { + return totalAmount.sub(claimedAmount); + } + + /** + * @dev See {IVestingEscrow-stop}. + */ + function stop(address beneficiary) external override onlyAdmin { + uint256 _totalAmount = totalAmount; + uint256 pulledAmount = _totalAmount.sub(claimedAmount); + require(pulledAmount > 0, "VestingEscrow: nothing to pull"); + + endTime = block.timestamp; + claimedAmount = _totalAmount; + + emit Stopped(msg.sender, beneficiary, pulledAmount); + token.safeTransfer(beneficiary, pulledAmount); + } + + /** + * @dev See {IVestingEscrow-claim}. + */ + function claim(address beneficiary, uint256 amount) external override whenNotPaused { + require(msg.sender == recipient, "VestingEscrow: access denied"); + require(amount > 0, "VestingEscrow: amount is zero"); + + uint256 _claimedAmount = claimedAmount; + uint256 claimable = vestedAmount().sub(_claimedAmount); + require(claimable >= amount, "VestingEscrow: invalid amount"); + + claimedAmount = _claimedAmount.add(amount); + emit Claimed(msg.sender, beneficiary, amount); + token.safeTransfer(beneficiary, amount); + } +} diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol new file mode 100644 index 00000000..9d4b8f50 --- /dev/null +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "../presets/OwnablePausableUpgradeable.sol"; +import "../interfaces/IVestingEscrowFactory.sol"; +import "../interfaces/IVestingEscrow.sol"; +import "hardhat/console.sol"; + + +/** + * @title VestingEscrowFactory + * + * @dev VestingEscrowFactory contract creates new vesting escrows and keeps track of total unclaimed balances of the users. + * Only admin can create new vesting escrows. + */ +contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeable { + using ClonesUpgradeable for address; + using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeMathUpgradeable for uint256; + + // @dev Address of the escrow implementation contract. + address public override escrowImplementation; + + // @dev Maps address of the beneficiary to its vesting escrows. + mapping(address => address[]) private escrows; + + /** + * @dev See {IVestingEscrowFactory-initialize}. + */ + function initialize(address _admin, address _escrowImplementation) external override initializer { + __OwnablePausableUpgradeable_init(_admin); + escrowImplementation = _escrowImplementation; + } + + /** + * @dev See {IVestingEscrowFactory-balanceOf}. + */ + function balanceOf(address account) external view override returns (uint256 total) { + for (uint256 i = 0; i < escrows[account].length; i++) { + total = total.add(IVestingEscrow(escrows[account][i]).unclaimedAmount()); + } + } + + /** + * @dev See {IVestingEscrowFactory-deployEscrow}. + */ + function deployEscrow( + address token, + address recipient, + uint256 amount, + uint256 vestingStart, + uint256 vestingDuration, + uint256 cliffLength + ) + external override onlyAdmin whenNotPaused returns (address escrow) + { + require(cliffLength <= vestingDuration, "VestingEscrowFactory: invalid cliff"); + + IERC20Upgradeable(token).safeTransferFrom(msg.sender, address(this), amount); + + escrow = escrowImplementation.clone(); + IERC20Upgradeable(token).safeApprove(escrow, amount); + escrows[recipient].push(escrow); + + if (vestingStart == 0) vestingStart = block.timestamp; + uint256 vestingEnd = vestingStart.add(vestingDuration); + IVestingEscrow(escrow).initialize( + msg.sender, + token, + recipient, + amount, + vestingStart, + vestingEnd, + cliffLength + ); + emit VestingEscrowCreated( + msg.sender, + token, + recipient, + escrow, + amount, + vestingStart, + vestingEnd, + cliffLength + ); + } +} diff --git a/deployments/vestings.js b/deployments/vestings.js new file mode 100644 index 00000000..470b2670 --- /dev/null +++ b/deployments/vestings.js @@ -0,0 +1,29 @@ +const { ethers, upgrades } = require('hardhat'); + +async function deployAndInitializeVestingEscrowFactory( + adminAddress, + escrowImplementationAddress +) { + const VestingEscrowFactory = await ethers.getContractFactory( + 'VestingEscrowFactory' + ); + const proxy = await upgrades.deployProxy(VestingEscrowFactory, [ + adminAddress, + escrowImplementationAddress, + ]); + await proxy.deployed(); + return proxy.address; +} + +async function deployVestingEscrow() { + // VestingEscrow is deployed without initialization as its clones are initialized + const VestingEscrow = await ethers.getContractFactory('VestingEscrow'); + const vestingEscrow = await VestingEscrow.deploy(); + await vestingEscrow.deployed(); + return vestingEscrow.address; +} + +module.exports = { + deployVestingEscrow, + deployAndInitializeVestingEscrowFactory, +}; diff --git a/test/vestings/VestingEscrow.test.js b/test/vestings/VestingEscrow.test.js new file mode 100644 index 00000000..d99fc12c --- /dev/null +++ b/test/vestings/VestingEscrow.test.js @@ -0,0 +1,265 @@ +const { expect } = require('chai'); +const { + expectRevert, + expectEvent, + ether, + send, + time, + BN, +} = require('@openzeppelin/test-helpers'); +const { upgradeContracts } = require('../../deployments'); +const { contractSettings } = require('../../deployments/settings'); +const { + stopImpersonatingAccount, + impersonateAccount, + resetFork, +} = require('../utils'); + +const VestingEscrowFactory = artifacts.require('VestingEscrowFactory'); +const VestingEscrow = artifacts.require('VestingEscrow'); +const StakeWiseToken = artifacts.require('StakeWiseToken'); + +contract('VestingEscrow', ([recipient, beneficiary, anyone]) => { + const admin = contractSettings.admin; + const totalAmount = ether('10000'); + const vestingDuration = time.duration.days(1460); + const cliffLength = time.duration.days(292); + let vestingEscrowFactory, stakeWiseToken, startTime, endTime, escrow; + + after(async () => stopImpersonatingAccount(admin)); + + beforeEach(async () => { + await impersonateAccount(admin); + await send.ether(anyone, admin, ether('5')); + + let contracts = await upgradeContracts(); + vestingEscrowFactory = await VestingEscrowFactory.at( + contracts.vestingEscrowFactory + ); + stakeWiseToken = await StakeWiseToken.at(contracts.stakeWiseToken); + await stakeWiseToken.approve(vestingEscrowFactory.address, totalAmount, { + from: admin, + }); + startTime = await time.latest(); + endTime = startTime.add(vestingDuration); + let receipt = await vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + totalAmount, + startTime, + vestingDuration, + cliffLength, + { + from: admin, + } + ); + escrow = await VestingEscrow.at(receipt.logs[2].args.escrow); + }); + + afterEach(async () => resetFork()); + + it('sets variables correctly upon initialization', async () => { + expect(await escrow.isAdmin(admin)).to.equal(true); + expect( + await escrow.getRoleMemberCount(await escrow.DEFAULT_ADMIN_ROLE()) + ).to.bignumber.equal(new BN(1)); + expect(await escrow.token()).to.equal(stakeWiseToken.address); + expect(await escrow.recipient()).to.equal(recipient); + expect(await escrow.totalAmount()).to.bignumber.equal(totalAmount); + expect(await escrow.claimedAmount()).to.bignumber.equal(new BN(0)); + expect(await escrow.startTime()).to.bignumber.equal(startTime); + expect(await escrow.endTime()).to.bignumber.equal(endTime); + expect(await escrow.cliffLength()).to.bignumber.equal(cliffLength); + expect(await escrow.unclaimedAmount()).to.bignumber.equal(totalAmount); + expect(await escrow.vestedAmount()).to.bignumber.equal(new BN(0)); + expect(await stakeWiseToken.balanceOf(escrow.address)).to.bignumber.equal( + totalAmount + ); + }); + + it('calculates vested amount correctly', async () => { + // no vested amount on start + expect(await escrow.vestedAmount()).to.bignumber.equal(new BN(0)); + + // half of cliff has passed -> still no vested amount + let halfCliff = cliffLength.div(new BN(2)); + await time.increase(halfCliff); + expect(await escrow.vestedAmount()).to.bignumber.equal(new BN(0)); + + // another half of cliff has passed -> 20% of vested amount claimable + await time.increase(halfCliff); + let vestedAmount = await escrow.vestedAmount(); + let expectedAmount = totalAmount.mul(new BN(20)).div(new BN(100)); + expect(vestedAmount).to.bignumber.gte(expectedAmount); + expect(vestedAmount).to.bignumber.lte(expectedAmount.add(ether('1'))); + + // another 20% of vesting time passed -> another 20% of vested amount claimable + await time.increase(cliffLength); + vestedAmount = await escrow.vestedAmount(); + expectedAmount = totalAmount.mul(new BN(40)).div(new BN(100)); + expect(vestedAmount).to.bignumber.gte(expectedAmount); + expect(vestedAmount).to.bignumber.lte(expectedAmount.add(ether('1'))); + + // vesting time has ended -> total vested amount unlocked + await time.increaseTo(endTime); + expect(await escrow.vestedAmount()).to.bignumber.eq(totalAmount); + }); + + describe('stoppage', () => { + it('admin can stop vesting escrow', async () => { + let receipt = await escrow.stop(beneficiary, { + from: admin, + }); + expectEvent(receipt, 'Stopped', { + sender: admin, + beneficiary, + amount: totalAmount, + }); + expect(await stakeWiseToken.balanceOf(beneficiary)).to.bignumber.equal( + totalAmount + ); + }); + + it('admin can stop vesting escrow when some amount was already claimed', async () => { + await time.increase(cliffLength); + let vestedAmount = await escrow.vestedAmount(); + await escrow.claim(recipient, vestedAmount, { + from: recipient, + }); + expect(await stakeWiseToken.balanceOf(recipient)).to.bignumber.equal( + vestedAmount + ); + + let receipt = await escrow.stop(beneficiary, { + from: admin, + }); + expectEvent(receipt, 'Stopped', { + sender: admin, + beneficiary, + amount: totalAmount.sub(vestedAmount), + }); + expect(await stakeWiseToken.balanceOf(beneficiary)).to.bignumber.equal( + totalAmount.sub(vestedAmount) + ); + }); + + it('fails to stop vesting escrow when full amount was already claimed', async () => { + await time.increaseTo(endTime); + await escrow.claim(recipient, totalAmount, { + from: recipient, + }); + expect(await stakeWiseToken.balanceOf(recipient)).to.bignumber.equal( + totalAmount + ); + + await expectRevert( + escrow.stop(beneficiary, { + from: admin, + }), + 'VestingEscrow: nothing to pull' + ); + expect(await stakeWiseToken.balanceOf(beneficiary)).to.bignumber.equal( + new BN(0) + ); + }); + + it('not admin cannot stop vesting escrow', async () => { + await expectRevert( + escrow.stop(beneficiary, { + from: recipient, + }), + 'OwnablePausable: access denied' + ); + }); + }); + + describe('claim', () => { + let vestedAmount = totalAmount.mul(new BN(20)).div(new BN(100)); + + beforeEach(async () => { + await time.increase(cliffLength); + }); + + it('recipient can claim unlocked tokens', async () => { + let receipt = await escrow.claim(beneficiary, vestedAmount, { + from: recipient, + }); + expectEvent(receipt, 'Claimed', { + sender: recipient, + beneficiary, + amount: vestedAmount, + }); + expect(await escrow.claimedAmount()).to.bignumber.equal(vestedAmount); + expect( + await vestingEscrowFactory.balanceOf(recipient) + ).to.bignumber.equal(totalAmount.sub(vestedAmount)); + expect(await stakeWiseToken.balanceOf(beneficiary)).to.bignumber.equal( + vestedAmount + ); + }); + + it('fails to claim unlocked tokens from not recipient address', async () => { + await expectRevert( + escrow.claim(beneficiary, vestedAmount, { + from: admin, + }), + 'VestingEscrow: access denied' + ); + }); + + it('fails to claim unlocked tokens when paused', async () => { + await escrow.pause({ from: admin }); + await expectRevert( + escrow.claim(beneficiary, vestedAmount, { + from: recipient, + }), + 'Pausable: paused' + ); + }); + + it('fails to claim with zero amount', async () => { + await expectRevert( + escrow.claim(beneficiary, new BN(0), { + from: recipient, + }), + 'VestingEscrow: amount is zero' + ); + }); + + it('fails to claim with invalid amount', async () => { + await expectRevert( + escrow.claim(beneficiary, vestedAmount.mul(new BN(2)), { + from: recipient, + }), + 'VestingEscrow: invalid amount' + ); + }); + + it('fails to claim multiple times', async () => { + await time.increaseTo(endTime); + + let receipt = await escrow.claim(beneficiary, totalAmount, { + from: recipient, + }); + expectEvent(receipt, 'Claimed', { + sender: recipient, + beneficiary, + amount: totalAmount, + }); + expect(await escrow.claimedAmount()).to.bignumber.equal(totalAmount); + expect( + await vestingEscrowFactory.balanceOf(recipient) + ).to.bignumber.equal(new BN(0)); + expect(await stakeWiseToken.balanceOf(beneficiary)).to.bignumber.equal( + totalAmount + ); + + await expectRevert( + escrow.claim(beneficiary, vestedAmount, { + from: recipient, + }), + 'VestingEscrow: invalid amount' + ); + }); + }); +}); diff --git a/test/vestings/VestingEscrowFactory.test.js b/test/vestings/VestingEscrowFactory.test.js new file mode 100644 index 00000000..3b4f437a --- /dev/null +++ b/test/vestings/VestingEscrowFactory.test.js @@ -0,0 +1,199 @@ +const { expect } = require('chai'); +const { + expectRevert, + expectEvent, + ether, + send, + time, + BN, +} = require('@openzeppelin/test-helpers'); +const { upgradeContracts } = require('../../deployments'); +const { contractSettings } = require('../../deployments/settings'); +const { + stopImpersonatingAccount, + impersonateAccount, + resetFork, +} = require('../utils'); + +const VestingEscrowFactory = artifacts.require('VestingEscrowFactory'); +const StakeWiseToken = artifacts.require('StakeWiseToken'); + +contract('VestingEscrowFactory', ([recipient, anyone]) => { + const admin = contractSettings.admin; + const vestedAmount = ether('10000'); + + let vestingEscrowFactory, stakeWiseToken; + + after(async () => stopImpersonatingAccount(admin)); + + beforeEach(async () => { + await impersonateAccount(admin); + await send.ether(anyone, admin, ether('5')); + + let contracts = await upgradeContracts(); + vestingEscrowFactory = await VestingEscrowFactory.at( + contracts.vestingEscrowFactory + ); + stakeWiseToken = await StakeWiseToken.at(contracts.stakeWiseToken); + }); + + afterEach(async () => resetFork()); + + it('fails to deploy escrow when paused', async () => { + await vestingEscrowFactory.pause({ from: admin }); + await expectRevert( + vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + 0, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ), + 'Pausable: paused' + ); + }); + + it('fails to deploy escrow when not admin', async () => { + await expectRevert( + vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + 0, + time.duration.years(4), + time.duration.days(180), + { + from: anyone, + } + ), + 'OwnablePausable: access denied' + ); + }); + + it('fails to deploy escrow with invalid cliff', async () => { + await expectRevert( + vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + 0, + time.duration.years(4), + time.duration.years(5), + { + from: admin, + } + ), + 'VestingEscrowFactory: invalid cliff' + ); + }); + + it('fails to deploy escrow with no token allowance', async () => { + await expectRevert( + vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + 0, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ), + 'SafeMath: subtraction overflow' + ); + }); + + it('deploys escrow for the recipient', async () => { + await stakeWiseToken.approve(vestingEscrowFactory.address, vestedAmount, { + from: admin, + }); + let startTime = await time.latest(); + let receipt = await vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + startTime, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ); + expect(await vestingEscrowFactory.balanceOf(recipient)).to.bignumber.equal( + vestedAmount + ); + expectEvent(receipt, 'VestingEscrowCreated', { + admin, + token: stakeWiseToken.address, + recipient, + totalAmount: vestedAmount, + startTime, + endTime: startTime.add(time.duration.years(4)), + cliffLength: time.duration.days(180), + }); + }); + + it('deploys multiple escrows for the recipient', async () => { + // deploy first escrow + await stakeWiseToken.approve(vestingEscrowFactory.address, vestedAmount, { + from: admin, + }); + let startTime = await time.latest(); + let receipt = await vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + startTime, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ); + expect(await vestingEscrowFactory.balanceOf(recipient)).to.bignumber.equal( + vestedAmount + ); + expectEvent(receipt, 'VestingEscrowCreated', { + admin, + token: stakeWiseToken.address, + recipient, + totalAmount: vestedAmount, + startTime, + endTime: startTime.add(time.duration.years(4)), + cliffLength: time.duration.days(180), + }); + + // deploy second escrow + await stakeWiseToken.approve(vestingEscrowFactory.address, vestedAmount, { + from: admin, + }); + receipt = await vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + recipient, + vestedAmount, + startTime, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ); + expect(await vestingEscrowFactory.balanceOf(recipient)).to.bignumber.equal( + vestedAmount.mul(new BN(2)) + ); + expectEvent(receipt, 'VestingEscrowCreated', { + admin, + token: stakeWiseToken.address, + recipient, + totalAmount: vestedAmount, + startTime, + endTime: startTime.add(time.duration.years(4)), + cliffLength: time.duration.days(180), + }); + }); +}); From c9d58a470725918709483c1ee75e2c87ef533a8d Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 00:55:06 +0300 Subject: [PATCH 08/25] Export vesting escrows abi, add vesting deployments --- abi/VestingEscrow.json | 627 ++++++++++++++++++++++++++++++++++ abi/VestingEscrowFactory.json | 535 +++++++++++++++++++++++++++++ deployments/index.js | 25 ++ hardhat.config.js | 2 + 4 files changed, 1189 insertions(+) create mode 100644 abi/VestingEscrow.json create mode 100644 abi/VestingEscrowFactory.json diff --git a/abi/VestingEscrow.json b/abi/VestingEscrow.json new file mode 100644 index 00000000..c7c84206 --- /dev/null +++ b/abi/VestingEscrow.json @@ -0,0 +1,627 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Claimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Stopped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAUSER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addPauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimedAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cliffLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "endTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_totalAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_endTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_cliffLength", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isAdmin", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isPauser", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "recipient", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removePauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "startTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + } + ], + "name": "stop", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unclaimedAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vestedAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/VestingEscrowFactory.json b/abi/VestingEscrowFactory.json new file mode 100644 index 00000000..78e2b3e8 --- /dev/null +++ b/abi/VestingEscrowFactory.json @@ -0,0 +1,535 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "admin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "escrow", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cliffLength", + "type": "uint256" + } + ], + "name": "VestingEscrowCreated", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAUSER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "addPauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vestingStart", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vestingDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cliffLength", + "type": "uint256" + } + ], + "name": "deployEscrow", + "outputs": [ + { + "internalType": "address", + "name": "escrow", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "escrowImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_admin", + "type": "address" + }, + { + "internalType": "address", + "name": "_escrowImplementation", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isAdmin", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "isPauser", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "removePauser", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/deployments/index.js b/deployments/index.js index f42bd9b8..7e7b4542 100644 --- a/deployments/index.js +++ b/deployments/index.js @@ -2,6 +2,10 @@ const hre = require('hardhat'); const { white, green } = require('chalk'); const { contractSettings, contracts } = require('./settings'); const { deployAndInitializeStakeWiseToken } = require('./tokens'); +const { + deployVestingEscrow, + deployAndInitializeVestingEscrowFactory, +} = require('./vestings'); function log(message) { if (hre.config != null && hre.config.suppressLogs !== true) { @@ -25,8 +29,29 @@ async function upgradeContracts() { ) ); + let vestingEscrowContractAddress = await deployVestingEscrow(); + log( + white( + `Deployed VestingEscrow contract: ${green(vestingEscrowContractAddress)}` + ) + ); + + let vestingEscrowFactoryContractAddress = await deployAndInitializeVestingEscrowFactory( + contractSettings.admin, + vestingEscrowContractAddress + ); + log( + white( + `Deployed VestingEscrow Factory contract: ${green( + vestingEscrowFactoryContractAddress + )}` + ) + ); + return { ...contracts, + vestingEscrowFactory: vestingEscrowFactoryContractAddress, + vestingEscrow: vestingEscrowContractAddress, stakeWiseToken: stakeWiseTokenContractAddress, }; } diff --git a/hardhat.config.js b/hardhat.config.js index 15b9c22c..b9e375f5 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -138,6 +138,8 @@ module.exports = { 'StakedEthToken', 'StakeWiseToken', 'Validators', + 'VestingEscrow', + 'VestingEscrowFactory', ], clear: true, flat: true, From d3ef00cfd2107917fe1ce8dbec453070dc634be4 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 29 Mar 2021 15:09:41 +0300 Subject: [PATCH 09/25] Suppress time solhint warnings --- contracts/vestings/VestingEscrow.sol | 3 +++ contracts/vestings/VestingEscrowFactory.sol | 1 + 2 files changed, 4 insertions(+) diff --git a/contracts/vestings/VestingEscrow.sol b/contracts/vestings/VestingEscrow.sol index 8e4d4865..2baf329d 100644 --- a/contracts/vestings/VestingEscrow.sol +++ b/contracts/vestings/VestingEscrow.sol @@ -70,9 +70,11 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { function vestedAmount() public view override returns (uint256) { uint256 _startTime = startTime; uint256 _endTime = endTime; + /* solhint-disable not-rely-on-time */ if (block.timestamp < _startTime.add(cliffLength)) return 0; else if (_endTime <= block.timestamp || _endTime <= _startTime) return totalAmount; return totalAmount.mul(block.timestamp.sub(_startTime)).div(_endTime.sub(_startTime)); + /* solhint-disable not-rely-on-time */ } /** @@ -90,6 +92,7 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { uint256 pulledAmount = _totalAmount.sub(claimedAmount); require(pulledAmount > 0, "VestingEscrow: nothing to pull"); + // solhint-disable-next-line not-rely-on-time endTime = block.timestamp; claimedAmount = _totalAmount; diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol index 9d4b8f50..da065f11 100644 --- a/contracts/vestings/VestingEscrowFactory.sol +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -67,6 +67,7 @@ contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeab IERC20Upgradeable(token).safeApprove(escrow, amount); escrows[recipient].push(escrow); + // solhint-disable-next-line not-rely-on-time if (vestingStart == 0) vestingStart = block.timestamp; uint256 vestingEnd = vestingStart.add(vestingDuration); IVestingEscrow(escrow).initialize( From e2abec21ff495f5c02a853532dcced8e9c38813c Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 31 Mar 2021 19:58:17 +0300 Subject: [PATCH 10/25] Implement Pool Escrow for ETH1 withdrawals --- abi/PoolEscrow.json | 144 ++++++++++++++++++++++ contracts/collectors/PoolEscrow.sol | 73 ++++++++++++ contracts/interfaces/IPoolEscrow.sol | 65 ++++++++++ deployments/collectors.js | 12 ++ deployments/index.js | 9 ++ hardhat.config.js | 1 + package.json | 2 +- test/pool/PoolEscrow.test.js | 171 +++++++++++++++++++++++++++ yarn.lock | 8 +- 9 files changed, 480 insertions(+), 5 deletions(-) create mode 100644 abi/PoolEscrow.json create mode 100644 contracts/collectors/PoolEscrow.sol create mode 100644 contracts/interfaces/IPoolEscrow.sol create mode 100644 deployments/collectors.js create mode 100644 test/pool/PoolEscrow.test.js diff --git a/abi/PoolEscrow.json b/abi/PoolEscrow.json new file mode 100644 index 00000000..a6d627cc --- /dev/null +++ b/abi/PoolEscrow.json @@ -0,0 +1,144 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferApplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "currentOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "futureOwner", + "type": "address" + } + ], + "name": "OwnershipTransferCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "payee", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "inputs": [], + "name": "applyOwnershipTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "commitOwnershipTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "futureOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "payee", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/contracts/collectors/PoolEscrow.sol b/contracts/collectors/PoolEscrow.sol new file mode 100644 index 00000000..f3cd6324 --- /dev/null +++ b/contracts/collectors/PoolEscrow.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts/utils/Address.sol"; +import "../interfaces/IPoolEscrow.sol"; + +/** + * @title PoolEscrow + * + * @dev PoolEscrow contract is used to receive transfers from ETH2 system contract for the pool validators. + * The withdrawal credentials of the Pool must be set to + * https://github.com/ethereum/eth2.0-specs/blob/v1.1.0/specs/phase0/validator.md#eth1_address_withdrawal_prefix + * using the address of this contract as a destination. + */ +contract PoolEscrow is IPoolEscrow { + using Address for address payable; + + // @dev The address of the current contract owner. + address public override owner; + + // @dev The address the ownership is planned to be transferred to. + address public override futureOwner; + + /** + * @dev Constructor for initializing the PoolEscrow contract. + * @param _owner - address of the contract owner. + */ + constructor(address _owner) { + owner = _owner; + emit OwnershipTransferApplied(address(0), _owner); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(owner == msg.sender, "PoolEscrow: caller is not the owner"); + _; + } + + /** + * @dev See {IPoolEscrow-commitOwnershipTransfer}. + */ + function commitOwnershipTransfer(address newOwner) external override onlyOwner { + require(newOwner != address(0), "PoolEscrow: new owner is the zero address"); + futureOwner = newOwner; + emit OwnershipTransferCommitted(msg.sender, newOwner); + } + + /** + * @dev See {IPoolEscrow-applyOwnershipTransfer}. + */ + function applyOwnershipTransfer() external override onlyOwner { + address newOwner = futureOwner; + require(newOwner != address(0), "PoolEscrow: new owner is the zero address"); + (owner, futureOwner) = (newOwner, address(0)); + emit OwnershipTransferApplied(msg.sender, newOwner); + } + + /** + * @dev See {IPoolEscrow-withdraw}. + */ + function withdraw(address payable payee, uint256 amount) external override onlyOwner { + payee.sendValue(amount); + emit Withdrawn(msg.sender, payee, amount); + } + + /** + * @dev Function for receiving withdrawals from ETH2 system contract. + */ + receive() external payable { } +} diff --git a/contracts/interfaces/IPoolEscrow.sol b/contracts/interfaces/IPoolEscrow.sol new file mode 100644 index 00000000..b8ea25fd --- /dev/null +++ b/contracts/interfaces/IPoolEscrow.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +/** + * @dev Interface of the PoolEscrow contract. + */ +interface IPoolEscrow { + /** + * @dev Event for tracking withdrawn ether. + * @param sender - the address of the transaction sender. + * @param payee - the address where the funds were transferred to. + * @param amount - the amount of ether transferred to payee. + */ + event Withdrawn(address indexed sender, address indexed payee, uint256 amount); + + /** + * @dev Event for tracking ownership transfer commits. + * @param currentOwner - the address of the current owner. + * @param futureOwner - the address the ownership is planned to be transferred to. + */ + event OwnershipTransferCommitted(address indexed currentOwner, address indexed futureOwner); + + /** + * @dev Event for tracking ownership transfers. + * @param previousOwner - the address the ownership was transferred from. + * @param newOwner - the address the ownership was transferred to. + */ + event OwnershipTransferApplied(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Function for retrieving the address of the current owner. + */ + function owner() external view returns (address); + + /** + * @dev Function for retrieving the address of the future owner. + */ + function futureOwner() external view returns (address); + + /** + * @dev Commit contract ownership transfer to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function commitOwnershipTransfer(address newOwner) external; + + /** + * @dev Apply contract ownership transfer to a new account (`futureOwner`). + * Can only be called by the current owner. + */ + function applyOwnershipTransfer() external; + + /** + * @dev Withdraw balance for a payee, forwarding all gas to the + * recipient. Can only be called by the current owner. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee - the address where the funds will be transferred to. + * @param amount - the amount of ether to transfer to payee. + */ + function withdraw(address payable payee, uint256 amount) external; +} diff --git a/deployments/collectors.js b/deployments/collectors.js new file mode 100644 index 00000000..48ce5533 --- /dev/null +++ b/deployments/collectors.js @@ -0,0 +1,12 @@ +const { ethers } = require('hardhat'); + +async function deployPoolEscrow(adminAddress) { + const PoolEscrow = await ethers.getContractFactory('PoolEscrow'); + const poolEscrow = await PoolEscrow.deploy(adminAddress); + await poolEscrow.deployed(); + return poolEscrow.address; +} + +module.exports = { + deployPoolEscrow, +}; diff --git a/deployments/index.js b/deployments/index.js index 7e7b4542..680e67a5 100644 --- a/deployments/index.js +++ b/deployments/index.js @@ -1,4 +1,5 @@ const hre = require('hardhat'); +const { deployPoolEscrow } = require('./collectors'); const { white, green } = require('chalk'); const { contractSettings, contracts } = require('./settings'); const { deployAndInitializeStakeWiseToken } = require('./tokens'); @@ -48,10 +49,18 @@ async function upgradeContracts() { ) ); + let poolEscrowContractAddress = await deployPoolEscrow( + contractSettings.admin + ); + log( + white(`Deployed PoolEscrow contract: ${green(poolEscrowContractAddress)}`) + ); + return { ...contracts, vestingEscrowFactory: vestingEscrowFactoryContractAddress, vestingEscrow: vestingEscrowContractAddress, + poolEscrow: poolEscrowContractAddress, stakeWiseToken: stakeWiseTokenContractAddress, }; } diff --git a/hardhat.config.js b/hardhat.config.js index b9e375f5..c6aeb2c9 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -140,6 +140,7 @@ module.exports = { 'Validators', 'VestingEscrow', 'VestingEscrowFactory', + 'PoolEscrow', ], clear: true, flat: true, diff --git a/package.json b/package.json index 9de4137f..dcb67f85 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@nomiclabs/hardhat-etherscan": "^2.1.1", "@nomiclabs/hardhat-truffle5": "^2.0.0", "@nomiclabs/hardhat-web3": "^2.0.0", - "@openzeppelin/contracts": "3.3.0", + "@openzeppelin/contracts": "3.4.1", "@openzeppelin/contracts-upgradeable": "3.4.1", "@openzeppelin/hardhat-upgrades": "^1.6.0", "@openzeppelin/test-helpers": "^0.5.10", diff --git a/test/pool/PoolEscrow.test.js b/test/pool/PoolEscrow.test.js new file mode 100644 index 00000000..10affb99 --- /dev/null +++ b/test/pool/PoolEscrow.test.js @@ -0,0 +1,171 @@ +const { expect } = require('chai'); +const { + expectRevert, + expectEvent, + ether, + send, + BN, + constants, + balance, +} = require('@openzeppelin/test-helpers'); +const { upgradeContracts } = require('../../deployments'); +const { contractSettings } = require('../../deployments/settings'); +const { + stopImpersonatingAccount, + impersonateAccount, + resetFork, +} = require('../utils'); + +const PoolEscrow = artifacts.require('PoolEscrow'); + +contract('PoolEscrow', ([anyone, newOwner, payee]) => { + const owner = contractSettings.admin; + let poolEscrow; + + after(async () => stopImpersonatingAccount(owner)); + + beforeEach(async () => { + await impersonateAccount(owner); + await send.ether(anyone, owner, ether('5')); + + let contracts = await upgradeContracts(); + poolEscrow = await PoolEscrow.at(contracts.poolEscrow); + }); + + afterEach(async () => resetFork()); + + it('sets owner on the contract creation', async () => { + expect(await poolEscrow.owner()).to.equal(owner); + }); + + it('can receive ETH transfers', async () => { + await send.ether(anyone, poolEscrow.address, ether('5')); + expect(await balance.current(poolEscrow.address)).to.bignumber.equal( + ether('5') + ); + }); + + describe('commit ownership transfer', () => { + it('owner can commit ownership transfer', async () => { + let receipt = await poolEscrow.commitOwnershipTransfer(newOwner, { + from: owner, + }); + expectEvent(receipt, 'OwnershipTransferCommitted', { + currentOwner: owner, + futureOwner: newOwner, + }); + expect(await poolEscrow.futureOwner()).to.equal(newOwner); + expect(await poolEscrow.owner()).to.equal(owner); + + // future owner cannot yet perform any actions + await expectRevert( + poolEscrow.withdraw(newOwner, ether('1'), { + from: newOwner, + }), + 'PoolEscrow: caller is not the owner' + ); + }); + + it('fails to commit ownership transfer if not an owner', async () => { + await expectRevert( + poolEscrow.commitOwnershipTransfer(newOwner, { + from: newOwner, + }), + 'PoolEscrow: caller is not the owner' + ); + }); + + it('fails to commit ownership transfer to zero address', async () => { + await expectRevert( + poolEscrow.commitOwnershipTransfer(constants.ZERO_ADDRESS, { + from: owner, + }), + 'PoolEscrow: new owner is the zero address' + ); + }); + }); + + describe('apply ownership transfer', () => { + it('owner can apply ownership transfer', async () => { + await poolEscrow.commitOwnershipTransfer(newOwner, { + from: owner, + }); + + let receipt = await poolEscrow.applyOwnershipTransfer({ + from: owner, + }); + expectEvent(receipt, 'OwnershipTransferApplied', { + previousOwner: owner, + newOwner: newOwner, + }); + expect(await poolEscrow.futureOwner()).to.equal(constants.ZERO_ADDRESS); + expect(await poolEscrow.owner()).to.equal(newOwner); + }); + + it('fails to apply ownership transfer if not an owner', async () => { + await poolEscrow.commitOwnershipTransfer(newOwner, { + from: owner, + }); + + await expectRevert( + poolEscrow.applyOwnershipTransfer({ + from: newOwner, + }), + 'PoolEscrow: caller is not the owner' + ); + }); + + it('fails to apply ownership transfer to zero address', async () => { + await expectRevert( + poolEscrow.applyOwnershipTransfer({ + from: owner, + }), + 'PoolEscrow: new owner is the zero address' + ); + }); + }); + + describe('withdraw ether', () => { + it('owner can withdraw ether from the escrow', async () => { + let amount = ether('5'); + await send.ether(anyone, poolEscrow.address, amount); + + let payeeBalance = await balance.current(payee); + let receipt = await poolEscrow.withdraw(payee, amount, { + from: owner, + }); + expectEvent(receipt, 'Withdrawn', { + sender: owner, + payee, + amount, + }); + expect(await balance.current(poolEscrow.address)).to.bignumber.equal( + new BN(0) + ); + expect(await balance.current(payee)).to.bignumber.equal( + payeeBalance.add(amount) + ); + }); + + it('fails to withdraw ether without admin role', async () => { + let amount = ether('5'); + await send.ether(anyone, poolEscrow.address, amount); + await expectRevert( + poolEscrow.withdraw(payee, amount, { + from: newOwner, + }), + 'PoolEscrow: caller is not the owner' + ); + }); + + it('fails to withdraw ether when not enough balance', async () => { + let amount = ether('5'); + await expectRevert( + poolEscrow.withdraw(payee, amount, { + from: owner, + }), + 'Address: insufficient balance' + ); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index c74668ad..6566360d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -548,10 +548,10 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1.tgz#38dfdfa86fda0a088c6fcdebe6870cfaf897b471" integrity sha512-wBGlUzEkOxcj/ghtcF2yKc8ZYh+PTUtm1mK38zoENulJ6aplij7eH8quo3lMugfzPJy+V6V5qI8QhdQmCn7hkQ== -"@openzeppelin/contracts@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.3.0.tgz#ffdb693c5c349fc33bba420248dd3ac0a2d7c408" - integrity sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw== +"@openzeppelin/contracts@3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1.tgz#03c891fec7f93be0ae44ed74e57a122a38732ce7" + integrity sha512-cUriqMauq1ylzP2TxePNdPqkwI7Le3Annh4K9rrpvKfSBB/bdW+Iu1ihBaTIABTAAJ85LmKL5SSPPL9ry8d1gQ== "@openzeppelin/hardhat-upgrades@^1.6.0": version "1.6.0" From 129eabe5cb6008e40691bbbb0b712b8baf0a00c3 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 31 Mar 2021 20:03:26 +0300 Subject: [PATCH 11/25] Fix link to eth2 specs --- contracts/collectors/PoolEscrow.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/collectors/PoolEscrow.sol b/contracts/collectors/PoolEscrow.sol index f3cd6324..f359a2ec 100644 --- a/contracts/collectors/PoolEscrow.sol +++ b/contracts/collectors/PoolEscrow.sol @@ -10,7 +10,7 @@ import "../interfaces/IPoolEscrow.sol"; * * @dev PoolEscrow contract is used to receive transfers from ETH2 system contract for the pool validators. * The withdrawal credentials of the Pool must be set to - * https://github.com/ethereum/eth2.0-specs/blob/v1.1.0/specs/phase0/validator.md#eth1_address_withdrawal_prefix + * https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.2/specs/phase0/validator.md#eth1_address_withdrawal_prefix * using the address of this contract as a destination. */ contract PoolEscrow is IPoolEscrow { From a3f26dd3b232399fc12cc091cef7182b4d51ead3 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 5 Apr 2021 10:35:46 +0300 Subject: [PATCH 12/25] Remove redundant hardhat console import --- contracts/vestings/VestingEscrowFactory.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol index da065f11..9f9afa71 100644 --- a/contracts/vestings/VestingEscrowFactory.sol +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -9,7 +9,6 @@ import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; import "../presets/OwnablePausableUpgradeable.sol"; import "../interfaces/IVestingEscrowFactory.sol"; import "../interfaces/IVestingEscrow.sol"; -import "hardhat/console.sol"; /** From a5e6c6796f78ee9895da849e81a4ebb512738e9a Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 5 Apr 2021 11:34:20 +0300 Subject: [PATCH 13/25] Remove tranfer, apply ownership from PoolEscrow --- abi/PoolEscrow.json | 53 +++----------------- abi/VestingEscrow.json | 2 +- contracts/collectors/PoolEscrow.sol | 25 +++------- contracts/interfaces/IPoolEscrow.sol | 28 ++--------- contracts/interfaces/IVestingEscrow.sol | 4 +- contracts/vestings/VestingEscrow.sol | 14 +++--- test/pool/PoolEscrow.test.js | 65 +++---------------------- 7 files changed, 36 insertions(+), 155 deletions(-) diff --git a/abi/PoolEscrow.json b/abi/PoolEscrow.json index a6d627cc..f5c56769 100644 --- a/abi/PoolEscrow.json +++ b/abi/PoolEscrow.json @@ -26,26 +26,7 @@ "type": "address" } ], - "name": "OwnershipTransferApplied", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "currentOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "futureOwner", - "type": "address" - } - ], - "name": "OwnershipTransferCommitted", + "name": "OwnershipTransferred", "type": "event" }, { @@ -75,27 +56,7 @@ }, { "inputs": [], - "name": "applyOwnershipTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "commitOwnershipTransfer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "futureOwner", + "name": "owner", "outputs": [ { "internalType": "address", @@ -107,16 +68,16 @@ "type": "function" }, { - "inputs": [], - "name": "owner", - "outputs": [ + "inputs": [ { "internalType": "address", - "name": "", + "name": "newOwner", "type": "address" } ], - "stateMutability": "view", + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", "type": "function" }, { diff --git a/abi/VestingEscrow.json b/abi/VestingEscrow.json index c7c84206..a6e3b4c1 100644 --- a/abi/VestingEscrow.json +++ b/abi/VestingEscrow.json @@ -570,7 +570,7 @@ "name": "token", "outputs": [ { - "internalType": "contract IERC20Upgradeable", + "internalType": "contract IERC20", "name": "", "type": "address" } diff --git a/contracts/collectors/PoolEscrow.sol b/contracts/collectors/PoolEscrow.sol index f359a2ec..75ee0df8 100644 --- a/contracts/collectors/PoolEscrow.sol +++ b/contracts/collectors/PoolEscrow.sol @@ -19,16 +19,13 @@ contract PoolEscrow is IPoolEscrow { // @dev The address of the current contract owner. address public override owner; - // @dev The address the ownership is planned to be transferred to. - address public override futureOwner; - /** * @dev Constructor for initializing the PoolEscrow contract. * @param _owner - address of the contract owner. */ constructor(address _owner) { owner = _owner; - emit OwnershipTransferApplied(address(0), _owner); + emit OwnershipTransferred(address(0), _owner); } /** @@ -40,30 +37,20 @@ contract PoolEscrow is IPoolEscrow { } /** - * @dev See {IPoolEscrow-commitOwnershipTransfer}. - */ - function commitOwnershipTransfer(address newOwner) external override onlyOwner { - require(newOwner != address(0), "PoolEscrow: new owner is the zero address"); - futureOwner = newOwner; - emit OwnershipTransferCommitted(msg.sender, newOwner); - } - - /** - * @dev See {IPoolEscrow-applyOwnershipTransfer}. + * @dev See {IPoolEscrow-transferOwnership}. */ - function applyOwnershipTransfer() external override onlyOwner { - address newOwner = futureOwner; + function transferOwnership(address newOwner) external override onlyOwner { require(newOwner != address(0), "PoolEscrow: new owner is the zero address"); - (owner, futureOwner) = (newOwner, address(0)); - emit OwnershipTransferApplied(msg.sender, newOwner); + emit OwnershipTransferred(owner, newOwner); + owner = newOwner; } /** * @dev See {IPoolEscrow-withdraw}. */ function withdraw(address payable payee, uint256 amount) external override onlyOwner { - payee.sendValue(amount); emit Withdrawn(msg.sender, payee, amount); + payee.sendValue(amount); } /** diff --git a/contracts/interfaces/IPoolEscrow.sol b/contracts/interfaces/IPoolEscrow.sol index b8ea25fd..dbe778a9 100644 --- a/contracts/interfaces/IPoolEscrow.sol +++ b/contracts/interfaces/IPoolEscrow.sol @@ -14,19 +14,12 @@ interface IPoolEscrow { */ event Withdrawn(address indexed sender, address indexed payee, uint256 amount); - /** - * @dev Event for tracking ownership transfer commits. - * @param currentOwner - the address of the current owner. - * @param futureOwner - the address the ownership is planned to be transferred to. - */ - event OwnershipTransferCommitted(address indexed currentOwner, address indexed futureOwner); - /** * @dev Event for tracking ownership transfers. - * @param previousOwner - the address the ownership was transferred from. - * @param newOwner - the address the ownership was transferred to. + * @param previousOwner - the address of the previous owner. + * @param newOwner - the address of the new owner. */ - event OwnershipTransferApplied(address indexed previousOwner, address indexed newOwner); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Function for retrieving the address of the current owner. @@ -34,21 +27,10 @@ interface IPoolEscrow { function owner() external view returns (address); /** - * @dev Function for retrieving the address of the future owner. - */ - function futureOwner() external view returns (address); - - /** - * @dev Commit contract ownership transfer to a new account (`newOwner`). - * Can only be called by the current owner. - */ - function commitOwnershipTransfer(address newOwner) external; - - /** - * @dev Apply contract ownership transfer to a new account (`futureOwner`). + * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ - function applyOwnershipTransfer() external; + function transferOwnership(address newOwner) external; /** * @dev Withdraw balance for a payee, forwarding all gas to the diff --git a/contracts/interfaces/IVestingEscrow.sol b/contracts/interfaces/IVestingEscrow.sol index cf10bb60..e632182a 100644 --- a/contracts/interfaces/IVestingEscrow.sol +++ b/contracts/interfaces/IVestingEscrow.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.5; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @dev Interface of the VestingEscrow contract. @@ -57,7 +57,7 @@ interface IVestingEscrow { /** * @dev Function for fetching the token contract address. */ - function token() external view returns (IERC20Upgradeable); + function token() external view returns (IERC20); /** * @dev Function for fetching the recipient address. diff --git a/contracts/vestings/VestingEscrow.sol b/contracts/vestings/VestingEscrow.sol index 2baf329d..7b5a8f5c 100644 --- a/contracts/vestings/VestingEscrow.sol +++ b/contracts/vestings/VestingEscrow.sol @@ -2,9 +2,9 @@ pragma solidity 0.7.5; -import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; import "../presets/OwnablePausableUpgradeable.sol"; import "../interfaces/IVestingEscrow.sol"; @@ -16,11 +16,11 @@ import "../interfaces/IVestingEscrow.sol"; * Admin user can stop the vesting and withdraw locked tokens. */ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { - using SafeERC20Upgradeable for IERC20Upgradeable; - using SafeMathUpgradeable for uint256; + using SafeERC20 for IERC20; + using SafeMath for uint256; // @dev Address of the token contract. - IERC20Upgradeable public override token; + IERC20 public override token; // @dev Address of the recipient. address public override recipient; @@ -55,7 +55,7 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { external override initializer { __OwnablePausableUpgradeable_init(_admin); - token = IERC20Upgradeable(_token); + token = IERC20(_token); recipient = _recipient; totalAmount = _totalAmount; startTime = _startTime; diff --git a/test/pool/PoolEscrow.test.js b/test/pool/PoolEscrow.test.js index 10affb99..75aa328a 100644 --- a/test/pool/PoolEscrow.test.js +++ b/test/pool/PoolEscrow.test.js @@ -45,79 +45,30 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { ); }); - describe('commit ownership transfer', () => { - it('owner can commit ownership transfer', async () => { - let receipt = await poolEscrow.commitOwnershipTransfer(newOwner, { + describe('transfer ownership', () => { + it('owner can transfer ownership', async () => { + let receipt = await poolEscrow.transferOwnership(newOwner, { from: owner, }); - expectEvent(receipt, 'OwnershipTransferCommitted', { - currentOwner: owner, - futureOwner: newOwner, - }); - expect(await poolEscrow.futureOwner()).to.equal(newOwner); - expect(await poolEscrow.owner()).to.equal(owner); - - // future owner cannot yet perform any actions - await expectRevert( - poolEscrow.withdraw(newOwner, ether('1'), { - from: newOwner, - }), - 'PoolEscrow: caller is not the owner' - ); - }); - - it('fails to commit ownership transfer if not an owner', async () => { - await expectRevert( - poolEscrow.commitOwnershipTransfer(newOwner, { - from: newOwner, - }), - 'PoolEscrow: caller is not the owner' - ); - }); - - it('fails to commit ownership transfer to zero address', async () => { - await expectRevert( - poolEscrow.commitOwnershipTransfer(constants.ZERO_ADDRESS, { - from: owner, - }), - 'PoolEscrow: new owner is the zero address' - ); - }); - }); - - describe('apply ownership transfer', () => { - it('owner can apply ownership transfer', async () => { - await poolEscrow.commitOwnershipTransfer(newOwner, { - from: owner, - }); - - let receipt = await poolEscrow.applyOwnershipTransfer({ - from: owner, - }); - expectEvent(receipt, 'OwnershipTransferApplied', { + expectEvent(receipt, 'OwnershipTransferred', { previousOwner: owner, newOwner: newOwner, }); - expect(await poolEscrow.futureOwner()).to.equal(constants.ZERO_ADDRESS); expect(await poolEscrow.owner()).to.equal(newOwner); }); - it('fails to apply ownership transfer if not an owner', async () => { - await poolEscrow.commitOwnershipTransfer(newOwner, { - from: owner, - }); - + it('fails to transfer ownership if not an owner', async () => { await expectRevert( - poolEscrow.applyOwnershipTransfer({ + poolEscrow.transferOwnership(newOwner, { from: newOwner, }), 'PoolEscrow: caller is not the owner' ); }); - it('fails to apply ownership transfer to zero address', async () => { + it('fails to transfer ownership to zero address', async () => { await expectRevert( - poolEscrow.applyOwnershipTransfer({ + poolEscrow.transferOwnership(constants.ZERO_ADDRESS, { from: owner, }), 'PoolEscrow: new owner is the zero address' From fbacc60241560d8e7c09d2408df2c0ca6c9b1717 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 5 Apr 2021 23:24:37 +0300 Subject: [PATCH 14/25] Implement Merkle Drop for the tokens distribution --- abi/MerkleDrop.json | 237 +++++++++++++++++++++++++++ contracts/interfaces/IMerkleDrop.sol | 73 +++++++++ contracts/merkles/MerkleDrop.sol | 80 +++++++++ hardhat.config.js | 1 + 4 files changed, 391 insertions(+) create mode 100644 abi/MerkleDrop.json create mode 100644 contracts/interfaces/IMerkleDrop.sol create mode 100644 contracts/merkles/MerkleDrop.sol diff --git a/abi/MerkleDrop.json b/abi/MerkleDrop.json new file mode 100644 index 00000000..c2b43d53 --- /dev/null +++ b/abi/MerkleDrop.json @@ -0,0 +1,237 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_merkleRoot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_duration", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Claimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Stopped", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "merkleProof", + "type": "bytes32[]" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "claimedBitMap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "expireTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "isClaimed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "merkleRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + } + ], + "name": "stop", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/contracts/interfaces/IMerkleDrop.sol b/contracts/interfaces/IMerkleDrop.sol new file mode 100644 index 00000000..1c65439c --- /dev/null +++ b/contracts/interfaces/IMerkleDrop.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/** + * @dev Interface of the MerkleDrop contract. + * Allows anyone to claim a token if they exist in a merkle root. + */ +interface IMerkleDrop { + /** + * @dev Event for tracking token claims. + * @param account - the address of the user that has claimed the tokens. + * @param index - the index of the user that has claimed the tokens. + * @param amount - the amount of tokens that the user has claimed. + */ + event Claimed(uint256 index, address indexed account, uint256 amount); + + /** + * @dev Event for tracking stoppage of the merkle drop. + * @param beneficiary - the address where the left tokens will be directed. + * @param amount - the amount of tokens that the were transferred to the beneficiary. + */ + event Stopped(address indexed beneficiary, uint256 amount); + + /** + * @dev Function for retrieving the current merkle root. + */ + function merkleRoot() external view returns (bytes32); + + /** + * @dev Function for retrieving the token contract address. + */ + function token() external view returns (IERC20); + + /** + * @dev Function for retrieving the expire timestamp of the merkle drop. + */ + function expireTimestamp() external view returns (uint256); + + /** + * @dev Function for checking the claimed bit map. + * @param wordIndex - the word index of te bit map. + */ + function claimedBitMap(uint256 wordIndex) external view returns (uint256); + + /** + * @dev Function for checking whether the tokens were already claimed. + * @param index - the index of the user that is part of the merkle root. + */ + function isClaimed(uint256 index) external view returns (bool); + + /** + * @dev Function for claiming the tokens to the account address. + * @param index - the index of the user that is part of the merkle root. + * @param account - the address of the user that is part of the merkle root. + * @param amount - the amount of tokens that the user was allocated. + * @param merkleProof - an array of hashes to verify whether the user is part of the merkle root. + */ + function claim( + uint256 index, + address account, + uint256 amount, + bytes32[] calldata merkleProof + ) external; + + /** + * @dev Function for stopping the expired merkle drop. Can only be called by the contract owner. + * @param beneficiary - the address of the beneficiary where the left tokens will be transferred. + */ + function stop(address beneficiary) external; +} diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol new file mode 100644 index 00000000..16a73a03 --- /dev/null +++ b/contracts/merkles/MerkleDrop.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity 0.7.5; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/cryptography/MerkleProof.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../interfaces/IMerkleDrop.sol"; + + +/** + * @title MerkleDrop + * + * @dev MerkleDrop contract allows users to claim their tokens by proving that they're part of the merkle tree. + */ +contract MerkleDrop is IMerkleDrop, Ownable { + using SafeERC20 for IERC20; + + // @dev Address of the token contract. + IERC20 public immutable override token; + + // @dev Merkle Root for proving tokens ownership. + bytes32 public immutable override merkleRoot; + + // @dev Expire timestamp for te merkle drop. + uint256 public immutable override expireTimestamp; + + // This is a packed array of booleans. + mapping(uint256 => uint256) public override claimedBitMap; + + constructor(address _token, bytes32 _merkleRoot, uint256 _duration) { + token = IERC20(_token); + merkleRoot = _merkleRoot; + expireTimestamp = block.timestamp + _duration; + } + + /** + * @dev See {IMerkleDrop-isClaimed}. + */ + function isClaimed(uint256 index) public view override returns (bool) { + uint256 claimedWordIndex = index / 256; + uint256 claimedBitIndex = index % 256; + uint256 claimedWord = claimedBitMap[claimedWordIndex]; + uint256 mask = (1 << claimedBitIndex); + return claimedWord & mask == mask; + } + + function _setClaimed(uint256 index) private { + uint256 claimedWordIndex = index / 256; + uint256 claimedBitIndex = index % 256; + claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex); + } + + /** + * @dev See {IMerkleDrop-claim}. + */ + function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override { + require(!isClaimed(index), 'MerkleDrop: drop already claimed'); + + // Verify the merkle proof. + bytes32 node = keccak256(abi.encodePacked(index, account, amount)); + require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDrop: invalid proof'); + + // Mark it claimed and send the token. + _setClaimed(index); + token.safeTransfer(account, amount); + emit Claimed(index, account, amount); + } + + /** + * @dev See {IMerkleDrop-stop}. + */ + function stop(address beneficiary) external override onlyOwner { + require(block.timestamp >= expireTimestamp, "MerkleDrop: not expired"); + uint256 amount = token.balanceOf(address(this)); + token.safeTransfer(beneficiary, amount); + emit Stopped(beneficiary, amount); + } +} diff --git a/hardhat.config.js b/hardhat.config.js index c6aeb2c9..dc447c3b 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -141,6 +141,7 @@ module.exports = { 'VestingEscrow', 'VestingEscrowFactory', 'PoolEscrow', + 'MerkleDrop', ], clear: true, flat: true, From 234101650cb68446d45894f58aa98a01ed49e5f7 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 5 Apr 2021 23:31:25 +0300 Subject: [PATCH 15/25] Add description to the constructor --- contracts/merkles/MerkleDrop.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol index 16a73a03..da9d114e 100644 --- a/contracts/merkles/MerkleDrop.sol +++ b/contracts/merkles/MerkleDrop.sol @@ -29,6 +29,12 @@ contract MerkleDrop is IMerkleDrop, Ownable { // This is a packed array of booleans. mapping(uint256 => uint256) public override claimedBitMap; + /** + * @dev Constructor for initializing the MerkleDrop contract. + * @param _token - address of the token contract. + * @param _merkleRoot - address of the merkle root. + * @param _duration - duration of the merkle drop in seconds. + */ constructor(address _token, bytes32 _merkleRoot, uint256 _duration) { token = IERC20(_token); merkleRoot = _merkleRoot; From 3bf12ae9795dcc5dbd2e94c3ab426b67968c2954 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Mon, 5 Apr 2021 23:44:26 +0300 Subject: [PATCH 16/25] Add link to uniswap merkle distributor --- contracts/merkles/MerkleDrop.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol index da9d114e..87897f03 100644 --- a/contracts/merkles/MerkleDrop.sol +++ b/contracts/merkles/MerkleDrop.sol @@ -13,6 +13,7 @@ import "../interfaces/IMerkleDrop.sol"; * @title MerkleDrop * * @dev MerkleDrop contract allows users to claim their tokens by proving that they're part of the merkle tree. + * Adopted from https://github.com/Uniswap/merkle-distributor/blob/0d478d722da2e5d95b7292fd8cbdb363d98e9a93/contracts/MerkleDistributor.sol */ contract MerkleDrop is IMerkleDrop, Ownable { using SafeERC20 for IERC20; From 9820a3f3359252212546d225a24136575767c410 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Tue, 6 Apr 2021 00:01:08 +0300 Subject: [PATCH 17/25] Add owner parameter to the constructor --- contracts/merkles/MerkleDrop.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol index 87897f03..c52e5551 100644 --- a/contracts/merkles/MerkleDrop.sol +++ b/contracts/merkles/MerkleDrop.sol @@ -32,14 +32,17 @@ contract MerkleDrop is IMerkleDrop, Ownable { /** * @dev Constructor for initializing the MerkleDrop contract. + * @param _owner - address of the contract owner. * @param _token - address of the token contract. * @param _merkleRoot - address of the merkle root. * @param _duration - duration of the merkle drop in seconds. */ - constructor(address _token, bytes32 _merkleRoot, uint256 _duration) { + constructor(address _owner, address _token, bytes32 _merkleRoot, uint256 _duration) { token = IERC20(_token); merkleRoot = _merkleRoot; + // solhint-disable-next-line not-rely-on-time expireTimestamp = block.timestamp + _duration; + transferOwnership(_owner); } /** @@ -63,11 +66,11 @@ contract MerkleDrop is IMerkleDrop, Ownable { * @dev See {IMerkleDrop-claim}. */ function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override { - require(!isClaimed(index), 'MerkleDrop: drop already claimed'); + require(!isClaimed(index), "MerkleDrop: drop already claimed"); // Verify the merkle proof. bytes32 node = keccak256(abi.encodePacked(index, account, amount)); - require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDrop: invalid proof'); + require(MerkleProof.verify(merkleProof, merkleRoot, node), "MerkleDrop: invalid proof"); // Mark it claimed and send the token. _setClaimed(index); From 64378078497af2ebe537cd6196d90d999771af9d Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Tue, 6 Apr 2021 00:03:39 +0300 Subject: [PATCH 18/25] Fix merkle drop stop linting --- contracts/merkles/MerkleDrop.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol index c52e5551..a09d0e41 100644 --- a/contracts/merkles/MerkleDrop.sol +++ b/contracts/merkles/MerkleDrop.sol @@ -82,6 +82,7 @@ contract MerkleDrop is IMerkleDrop, Ownable { * @dev See {IMerkleDrop-stop}. */ function stop(address beneficiary) external override onlyOwner { + // solhint-disable-next-line not-rely-on-time require(block.timestamp >= expireTimestamp, "MerkleDrop: not expired"); uint256 amount = token.balanceOf(address(this)); token.safeTransfer(beneficiary, amount); From dbd2cbc8f48710b16f8c3c3d7211fb46da23e5b9 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 18:43:57 +0300 Subject: [PATCH 19/25] PEE-1: fix PoolEscrow withdrawal to zero address --- contracts/collectors/PoolEscrow.sol | 1 + test/pool/PoolEscrow.test.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/contracts/collectors/PoolEscrow.sol b/contracts/collectors/PoolEscrow.sol index 75ee0df8..d2946593 100644 --- a/contracts/collectors/PoolEscrow.sol +++ b/contracts/collectors/PoolEscrow.sol @@ -49,6 +49,7 @@ contract PoolEscrow is IPoolEscrow { * @dev See {IPoolEscrow-withdraw}. */ function withdraw(address payable payee, uint256 amount) external override onlyOwner { + require(payee != address(0), "PoolEscrow: payee is the zero address"); emit Withdrawn(msg.sender, payee, amount); payee.sendValue(amount); } diff --git a/test/pool/PoolEscrow.test.js b/test/pool/PoolEscrow.test.js index 75aa328a..ced1c96a 100644 --- a/test/pool/PoolEscrow.test.js +++ b/test/pool/PoolEscrow.test.js @@ -109,6 +109,17 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { ); }); + it('fails to withdraw ether with invalid payee address', async () => { + let amount = ether('5'); + await send.ether(anyone, poolEscrow.address, amount); + await expectRevert( + poolEscrow.withdraw(constants.ZERO_ADDRESS, amount, { + from: owner, + }), + 'PoolEscrow: payee is the zero address' + ); + }); + it('fails to withdraw ether when not enough balance', async () => { let amount = ether('5'); await expectRevert( From 1a6cce8379312e0e4fc5473bbb8da877d76bfc08 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 20:09:10 +0300 Subject: [PATCH 20/25] VEE-1: add checks for zero addresses to vesting escrows --- contracts/vestings/VestingEscrow.sol | 2 ++ contracts/vestings/VestingEscrowFactory.sol | 1 + test/vestings/VestingEscrow.test.js | 19 +++++++++++ test/vestings/VestingEscrowFactory.test.js | 35 +++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/contracts/vestings/VestingEscrow.sol b/contracts/vestings/VestingEscrow.sol index 7b5a8f5c..5e75abe0 100644 --- a/contracts/vestings/VestingEscrow.sol +++ b/contracts/vestings/VestingEscrow.sol @@ -88,6 +88,7 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { * @dev See {IVestingEscrow-stop}. */ function stop(address beneficiary) external override onlyAdmin { + require(beneficiary != address(0), "PoolEscrow: beneficiary is the zero address"); uint256 _totalAmount = totalAmount; uint256 pulledAmount = _totalAmount.sub(claimedAmount); require(pulledAmount > 0, "VestingEscrow: nothing to pull"); @@ -104,6 +105,7 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { * @dev See {IVestingEscrow-claim}. */ function claim(address beneficiary, uint256 amount) external override whenNotPaused { + require(beneficiary != address(0), "PoolEscrow: beneficiary is the zero address"); require(msg.sender == recipient, "VestingEscrow: access denied"); require(amount > 0, "VestingEscrow: amount is zero"); diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol index 9f9afa71..2cbd1a34 100644 --- a/contracts/vestings/VestingEscrowFactory.sol +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -59,6 +59,7 @@ contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeab external override onlyAdmin whenNotPaused returns (address escrow) { require(cliffLength <= vestingDuration, "VestingEscrowFactory: invalid cliff"); + require(recipient != address(0), "PoolEscrow: recipient is the zero address"); IERC20Upgradeable(token).safeTransferFrom(msg.sender, address(this), amount); diff --git a/test/vestings/VestingEscrow.test.js b/test/vestings/VestingEscrow.test.js index d99fc12c..fe145d8d 100644 --- a/test/vestings/VestingEscrow.test.js +++ b/test/vestings/VestingEscrow.test.js @@ -6,6 +6,7 @@ const { send, time, BN, + constants, } = require('@openzeppelin/test-helpers'); const { upgradeContracts } = require('../../deployments'); const { contractSettings } = require('../../deployments/settings'); @@ -171,6 +172,15 @@ contract('VestingEscrow', ([recipient, beneficiary, anyone]) => { 'OwnablePausable: access denied' ); }); + + it('fails to stop vesting escrow with invalid beneficiary address', async () => { + await expectRevert( + escrow.stop(constants.ZERO_ADDRESS, { + from: admin, + }), + 'PoolEscrow: beneficiary is the zero address' + ); + }); }); describe('claim', () => { @@ -207,6 +217,15 @@ contract('VestingEscrow', ([recipient, beneficiary, anyone]) => { ); }); + it('fails to claim unlocked tokens with invalid beneficiary address', async () => { + await expectRevert( + escrow.claim(constants.ZERO_ADDRESS, vestedAmount, { + from: recipient, + }), + 'PoolEscrow: beneficiary is the zero address' + ); + }); + it('fails to claim unlocked tokens when paused', async () => { await escrow.pause({ from: admin }); await expectRevert( diff --git a/test/vestings/VestingEscrowFactory.test.js b/test/vestings/VestingEscrowFactory.test.js index 3b4f437a..24fb0780 100644 --- a/test/vestings/VestingEscrowFactory.test.js +++ b/test/vestings/VestingEscrowFactory.test.js @@ -6,6 +6,7 @@ const { send, time, BN, + constants, } = require('@openzeppelin/test-helpers'); const { upgradeContracts } = require('../../deployments'); const { contractSettings } = require('../../deployments/settings'); @@ -108,6 +109,40 @@ contract('VestingEscrowFactory', ([recipient, anyone]) => { ); }); + it('fails to deploy escrow with invalid recipient', async () => { + await expectRevert( + vestingEscrowFactory.deployEscrow( + stakeWiseToken.address, + constants.ZERO_ADDRESS, + vestedAmount, + 0, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ), + 'PoolEscrow: recipient is the zero address' + ); + }); + + it('fails to deploy escrow with invalid token', async () => { + await expectRevert( + vestingEscrowFactory.deployEscrow( + constants.ZERO_ADDRESS, + recipient, + vestedAmount, + 0, + time.duration.years(4), + time.duration.days(180), + { + from: admin, + } + ), + 'Address: call to non-contract' + ); + }); + it('deploys escrow for the recipient', async () => { await stakeWiseToken.approve(vestingEscrowFactory.address, vestedAmount, { from: admin, From 41b47498b884072f3d8bb20fe900ddbae9026750 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 20:14:58 +0300 Subject: [PATCH 21/25] VEE-2: fix ambigious check in Vesting Escrow --- contracts/vestings/VestingEscrow.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/vestings/VestingEscrow.sol b/contracts/vestings/VestingEscrow.sol index 5e75abe0..19446121 100644 --- a/contracts/vestings/VestingEscrow.sol +++ b/contracts/vestings/VestingEscrow.sol @@ -72,7 +72,7 @@ contract VestingEscrow is IVestingEscrow, OwnablePausableUpgradeable { uint256 _endTime = endTime; /* solhint-disable not-rely-on-time */ if (block.timestamp < _startTime.add(cliffLength)) return 0; - else if (_endTime <= block.timestamp || _endTime <= _startTime) return totalAmount; + else if (_endTime <= block.timestamp) return totalAmount; return totalAmount.mul(block.timestamp.sub(_startTime)).div(_endTime.sub(_startTime)); /* solhint-disable not-rely-on-time */ } From 6d14bf4eeb415a40231ce6624c99435b84f8dae7 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 20:17:53 +0300 Subject: [PATCH 22/25] VEF-1: gas optimization for VestingEscrowFactory balanceOf --- contracts/vestings/VestingEscrowFactory.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol index 2cbd1a34..c73cd72d 100644 --- a/contracts/vestings/VestingEscrowFactory.sol +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -40,7 +40,8 @@ contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeab * @dev See {IVestingEscrowFactory-balanceOf}. */ function balanceOf(address account) external view override returns (uint256 total) { - for (uint256 i = 0; i < escrows[account].length; i++) { + uint256 escrowsCount = escrows[account].length; + for (uint256 i = 0; i < escrowsCount; i++) { total = total.add(IVestingEscrow(escrows[account][i]).unclaimedAmount()); } } From f865adf3b90818e6d8b9d8af01080842fb24aa16 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 20:24:17 +0300 Subject: [PATCH 23/25] VEF-2: add re-entrancy guard to the VestingEscrowFactory --- contracts/vestings/VestingEscrowFactory.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/vestings/VestingEscrowFactory.sol b/contracts/vestings/VestingEscrowFactory.sol index c73cd72d..1f9664e6 100644 --- a/contracts/vestings/VestingEscrowFactory.sol +++ b/contracts/vestings/VestingEscrowFactory.sol @@ -5,6 +5,7 @@ pragma solidity 0.7.5; import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; import "../presets/OwnablePausableUpgradeable.sol"; import "../interfaces/IVestingEscrowFactory.sol"; @@ -17,7 +18,7 @@ import "../interfaces/IVestingEscrow.sol"; * @dev VestingEscrowFactory contract creates new vesting escrows and keeps track of total unclaimed balances of the users. * Only admin can create new vesting escrows. */ -contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeable { +contract VestingEscrowFactory is IVestingEscrowFactory, ReentrancyGuardUpgradeable, OwnablePausableUpgradeable { using ClonesUpgradeable for address; using SafeERC20Upgradeable for IERC20Upgradeable; using SafeMathUpgradeable for uint256; @@ -57,7 +58,7 @@ contract VestingEscrowFactory is IVestingEscrowFactory, OwnablePausableUpgradeab uint256 vestingDuration, uint256 cliffLength ) - external override onlyAdmin whenNotPaused returns (address escrow) + external override onlyAdmin whenNotPaused nonReentrant returns (address escrow) { require(cliffLength <= vestingDuration, "VestingEscrowFactory: invalid cliff"); require(recipient != address(0), "PoolEscrow: recipient is the zero address"); From 9ec1923bce1d26a7c1dd65f2518080d77f5694da Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Wed, 14 Apr 2021 23:19:42 +0300 Subject: [PATCH 24/25] Check for zero address beneficiary --- contracts/merkles/MerkleDrop.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/merkles/MerkleDrop.sol b/contracts/merkles/MerkleDrop.sol index a09d0e41..17e46d73 100644 --- a/contracts/merkles/MerkleDrop.sol +++ b/contracts/merkles/MerkleDrop.sol @@ -82,6 +82,7 @@ contract MerkleDrop is IMerkleDrop, Ownable { * @dev See {IMerkleDrop-stop}. */ function stop(address beneficiary) external override onlyOwner { + require(beneficiary != address(0), "MerkleDrop: beneficiary is the zero address"); // solhint-disable-next-line not-rely-on-time require(block.timestamp >= expireTimestamp, "MerkleDrop: not expired"); uint256 amount = token.balanceOf(address(this)); From 832333d24eadcb091a82fea9c2f74115a7b19471 Mon Sep 17 00:00:00 2001 From: Dmitri Tsumak Date: Sat, 17 Apr 2021 19:42:56 +0300 Subject: [PATCH 25/25] PEE-02: require new owner to commit his ownership --- abi/PoolEscrow.json | 53 ++++++++++++++++--- contracts/collectors/PoolEscrow.sol | 26 ++++++--- contracts/interfaces/IPoolEscrow.sol | 28 ++++++++-- test/pool/PoolEscrow.test.js | 79 +++++++++++++++++++++++----- 4 files changed, 156 insertions(+), 30 deletions(-) diff --git a/abi/PoolEscrow.json b/abi/PoolEscrow.json index f5c56769..a6d627cc 100644 --- a/abi/PoolEscrow.json +++ b/abi/PoolEscrow.json @@ -26,7 +26,26 @@ "type": "address" } ], - "name": "OwnershipTransferred", + "name": "OwnershipTransferApplied", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "currentOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "futureOwner", + "type": "address" + } + ], + "name": "OwnershipTransferCommitted", "type": "event" }, { @@ -56,7 +75,27 @@ }, { "inputs": [], - "name": "owner", + "name": "applyOwnershipTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "commitOwnershipTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "futureOwner", "outputs": [ { "internalType": "address", @@ -68,16 +107,16 @@ "type": "function" }, { - "inputs": [ + "inputs": [], + "name": "owner", + "outputs": [ { "internalType": "address", - "name": "newOwner", + "name": "", "type": "address" } ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { diff --git a/contracts/collectors/PoolEscrow.sol b/contracts/collectors/PoolEscrow.sol index d2946593..da4edee3 100644 --- a/contracts/collectors/PoolEscrow.sol +++ b/contracts/collectors/PoolEscrow.sol @@ -19,13 +19,16 @@ contract PoolEscrow is IPoolEscrow { // @dev The address of the current contract owner. address public override owner; + // @dev The address the ownership is planned to be transferred to. + address public override futureOwner; + /** * @dev Constructor for initializing the PoolEscrow contract. * @param _owner - address of the contract owner. */ constructor(address _owner) { owner = _owner; - emit OwnershipTransferred(address(0), _owner); + emit OwnershipTransferApplied(address(0), _owner); } /** @@ -37,12 +40,23 @@ contract PoolEscrow is IPoolEscrow { } /** - * @dev See {IPoolEscrow-transferOwnership}. + * @dev See {IPoolEscrow-commitOwnershipTransfer}. + */ + function commitOwnershipTransfer(address newOwner) external override onlyOwner { + // can be zero address to reset incorrect future owner + futureOwner = newOwner; + emit OwnershipTransferCommitted(msg.sender, newOwner); + } + + /** + * @dev See {IPoolEscrow-applyOwnershipTransfer}. */ - function transferOwnership(address newOwner) external override onlyOwner { - require(newOwner != address(0), "PoolEscrow: new owner is the zero address"); - emit OwnershipTransferred(owner, newOwner); - owner = newOwner; + function applyOwnershipTransfer() external override { + address newOwner = futureOwner; + require(newOwner == msg.sender, "PoolEscrow: caller is not the future owner"); + + emit OwnershipTransferApplied(owner, newOwner); + (owner, futureOwner) = (newOwner, address(0)); } /** diff --git a/contracts/interfaces/IPoolEscrow.sol b/contracts/interfaces/IPoolEscrow.sol index dbe778a9..e7bf8d16 100644 --- a/contracts/interfaces/IPoolEscrow.sol +++ b/contracts/interfaces/IPoolEscrow.sol @@ -14,12 +14,19 @@ interface IPoolEscrow { */ event Withdrawn(address indexed sender, address indexed payee, uint256 amount); + /** + * @dev Event for tracking ownership transfer commits. + * @param currentOwner - the address of the current owner. + * @param futureOwner - the address the ownership is planned to be transferred to. + */ + event OwnershipTransferCommitted(address indexed currentOwner, address indexed futureOwner); + /** * @dev Event for tracking ownership transfers. - * @param previousOwner - the address of the previous owner. - * @param newOwner - the address of the new owner. + * @param previousOwner - the address the ownership was transferred from. + * @param newOwner - the address the ownership was transferred to. */ - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event OwnershipTransferApplied(address indexed previousOwner, address indexed newOwner); /** * @dev Function for retrieving the address of the current owner. @@ -27,10 +34,21 @@ interface IPoolEscrow { function owner() external view returns (address); /** - * @dev Transfers ownership of the contract to a new account (`newOwner`). + * @dev Function for retrieving the address of the future owner. + */ + function futureOwner() external view returns (address); + + /** + * @dev Commit contract ownership transfer to a new account (`newOwner`). * Can only be called by the current owner. */ - function transferOwnership(address newOwner) external; + function commitOwnershipTransfer(address newOwner) external; + + /** + * @dev Apply contract ownership transfer to a new account (`futureOwner`). + * Can only be called by the future owner. + */ + function applyOwnershipTransfer() external; /** * @dev Withdraw balance for a payee, forwarding all gas to the diff --git a/test/pool/PoolEscrow.test.js b/test/pool/PoolEscrow.test.js index ced1c96a..b8f1e811 100644 --- a/test/pool/PoolEscrow.test.js +++ b/test/pool/PoolEscrow.test.js @@ -45,33 +45,88 @@ contract('PoolEscrow', ([anyone, newOwner, payee]) => { ); }); - describe('transfer ownership', () => { - it('owner can transfer ownership', async () => { - let receipt = await poolEscrow.transferOwnership(newOwner, { + describe('commit ownership transfer', () => { + it('owner can commit ownership transfer', async () => { + let receipt = await poolEscrow.commitOwnershipTransfer(newOwner, { from: owner, }); - expectEvent(receipt, 'OwnershipTransferred', { - previousOwner: owner, - newOwner: newOwner, + expectEvent(receipt, 'OwnershipTransferCommitted', { + currentOwner: owner, + futureOwner: newOwner, }); - expect(await poolEscrow.owner()).to.equal(newOwner); + expect(await poolEscrow.futureOwner()).to.equal(newOwner); + expect(await poolEscrow.owner()).to.equal(owner); + + // future owner cannot yet perform any actions + await expectRevert( + poolEscrow.withdraw(newOwner, ether('1'), { + from: newOwner, + }), + 'PoolEscrow: caller is not the owner' + ); }); - it('fails to transfer ownership if not an owner', async () => { + it('fails to commit ownership transfer if not an owner', async () => { await expectRevert( - poolEscrow.transferOwnership(newOwner, { + poolEscrow.commitOwnershipTransfer(newOwner, { from: newOwner, }), 'PoolEscrow: caller is not the owner' ); }); - it('fails to transfer ownership to zero address', async () => { + it('can commit ownership transfer to zero address', async () => { + let receipt = await poolEscrow.commitOwnershipTransfer( + constants.ZERO_ADDRESS, + { + from: owner, + } + ); + expectEvent(receipt, 'OwnershipTransferCommitted', { + currentOwner: owner, + futureOwner: constants.ZERO_ADDRESS, + }); + expect(await poolEscrow.futureOwner()).to.equal(constants.ZERO_ADDRESS); + expect(await poolEscrow.owner()).to.equal(owner); + }); + }); + + describe('apply ownership transfer', () => { + it('future owner can apply ownership transfer', async () => { + await poolEscrow.commitOwnershipTransfer(newOwner, { + from: owner, + }); + + let receipt = await poolEscrow.applyOwnershipTransfer({ + from: newOwner, + }); + expectEvent(receipt, 'OwnershipTransferApplied', { + previousOwner: owner, + newOwner, + }); + expect(await poolEscrow.futureOwner()).to.equal(constants.ZERO_ADDRESS); + expect(await poolEscrow.owner()).to.equal(newOwner); + }); + + it('fails to apply ownership transfer if not a future owner', async () => { + await poolEscrow.commitOwnershipTransfer(newOwner, { + from: owner, + }); + await expectRevert( - poolEscrow.transferOwnership(constants.ZERO_ADDRESS, { + poolEscrow.applyOwnershipTransfer({ from: owner, }), - 'PoolEscrow: new owner is the zero address' + 'PoolEscrow: caller is not the future owner' + ); + }); + + it('fails to apply ownership transfer if not committed', async () => { + await expectRevert( + poolEscrow.applyOwnershipTransfer({ + from: newOwner, + }), + 'PoolEscrow: caller is not the future owner' ); }); });