diff --git a/solidity/ecdsa/contracts/WalletRegistry.sol b/solidity/ecdsa/contracts/WalletRegistry.sol index dc08d55259..d2f7211ff7 100644 --- a/solidity/ecdsa/contracts/WalletRegistry.sol +++ b/solidity/ecdsa/contracts/WalletRegistry.sol @@ -336,7 +336,7 @@ contract WalletRegistry is /// If there is a pending authorization decrease request, it is /// overwritten. /// - /// @dev Should only be callable by T staking contract. + /// @dev Can only be called by T staking contract. function authorizationDecreaseRequested( address stakingProvider, uint96 fromAmount, @@ -350,7 +350,7 @@ contract WalletRegistry is } /// @notice Approves the previously registered authorization decrease - /// request. Reverts if authorization decrease delay have not passed + /// request. Reverts if authorization decrease delay has not passed /// yet or if the authorization decrease was not requested for the /// given staking provider. function approveAuthorizationDecrease(address stakingProvider) external { diff --git a/solidity/ecdsa/package.json b/solidity/ecdsa/package.json index b291da2bd0..bb10acae05 100644 --- a/solidity/ecdsa/package.json +++ b/solidity/ecdsa/package.json @@ -63,7 +63,7 @@ }, "dependencies": { "@keep-network/random-beacon": "development", - "@keep-network/sortition-pools": "^2.0.0-dev.0", + "@keep-network/sortition-pools": "^2.0.0-pre.6", "@openzeppelin/contracts": "^4.4.1", "@threshold-network/solidity-contracts": ">1.1.0-dev <1.1.0-ropsten" }, diff --git a/solidity/ecdsa/yarn.lock b/solidity/ecdsa/yarn.lock index 68dcfd5a2d..412d7a2f5d 100644 --- a/solidity/ecdsa/yarn.lock +++ b/solidity/ecdsa/yarn.lock @@ -617,22 +617,15 @@ resolved "https://registry.yarnpkg.com/@keep-network/random-beacon/-/random-beacon-2.0.0-dev.3.tgz#0aee6913ffde3806d3652297155fb2c1bbff6a50" integrity sha512-cqpvGGOBoXqoUxVAqcF5rmlEg8lbmj+LRWzOYW4jaweWVxzRBG2DbaaZ7frHXfJHZ3sicr4cLhlhcqbIuiubYA== dependencies: - "@keep-network/sortition-pools" "1.2.0-dev.24" + "@keep-network/sortition-pools" "^2.0.0-pre.6" "@openzeppelin/contracts" "^4.4.2" "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" + "@threshold-network/solidity-contracts" ">1.1.0-dev <1.1.0-ropsten" -"@keep-network/sortition-pools@1.2.0-dev.24": - version "1.2.0-dev.24" - resolved "https://registry.yarnpkg.com/@keep-network/sortition-pools/-/sortition-pools-1.2.0-dev.24.tgz#ed2bd34297ba3832d8981f2f5705489caece2115" - integrity sha512-k6PgZ3Svv4nCAP3bE3jfPoz7V6WUf9f8qKSrlMUGB6TkrgoSwg7I9mrC5UCawTrd3QL86FI/3hZt+4FJvZt74w== - dependencies: - "@openzeppelin/contracts" "^4.3.2" - "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" - -"@keep-network/sortition-pools@^2.0.0-dev.0": - version "2.0.0-dev.0" - resolved "https://registry.yarnpkg.com/@keep-network/sortition-pools/-/sortition-pools-2.0.0-dev.0.tgz#c1ec90962ab809e224338c8501aee4ed6c3c4a4f" - integrity sha512-zOyIQ2g5n0wCFlyKktyrGxNAgMGw9KgGYOEz1e93+n+Hs/3wl2olbW6xI5ZJ4lTETkBq3gO+KtkQysHc8rglBg== +"@keep-network/sortition-pools@^2.0.0-pre.6": + version "2.0.0-pre.6" + resolved "https://registry.yarnpkg.com/@keep-network/sortition-pools/-/sortition-pools-2.0.0-pre.6.tgz#72ca66cc0c476a21644d9b2342f7136268c802eb" + integrity sha512-i+naD1W10pxCRjzJ63yeNJgpokpWzr7eTDI4TRCT1opIRyYCUPa6VNF+U3WGGToM5PtgrBHIe1TZbL7nx8LTSw== dependencies: "@openzeppelin/contracts" "^4.3.2" "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" diff --git a/solidity/random-beacon/.eslintignore b/solidity/random-beacon/.eslintignore index 3c3629e647..6604016c57 100644 --- a/solidity/random-beacon/.eslintignore +++ b/solidity/random-beacon/.eslintignore @@ -1 +1,8 @@ -node_modules +artifacts/ +build/ +cache/ +deployments/ +export/ +hardhat-dependency-compiler/ +typechain/ +export.json diff --git a/solidity/random-beacon/.prettierignore b/solidity/random-beacon/.prettierignore index 16a51c5592..e1bc981305 100644 --- a/solidity/random-beacon/.prettierignore +++ b/solidity/random-beacon/.prettierignore @@ -2,5 +2,6 @@ artifacts/ build/ cache/ deployments/ -export.json +hardhat-dependency-compiler/ typechain/ +export.json diff --git a/solidity/random-beacon/.solhintignore b/solidity/random-beacon/.solhintignore index 40b878db5b..ef4020b604 100644 --- a/solidity/random-beacon/.solhintignore +++ b/solidity/random-beacon/.solhintignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +hardhat-dependency-compiler/ +node_modules/ diff --git a/solidity/random-beacon/contracts/RandomBeacon.sol b/solidity/random-beacon/contracts/RandomBeacon.sol index 4e80cf073b..b0220f2bdc 100644 --- a/solidity/random-beacon/contracts/RandomBeacon.sol +++ b/solidity/random-beacon/contracts/RandomBeacon.sol @@ -15,15 +15,19 @@ pragma solidity ^0.8.9; import "./api/IRandomBeacon.sol"; -import "./libraries/Authorization.sol"; import "./libraries/Groups.sol"; import "./libraries/Relay.sol"; import "./libraries/Groups.sol"; import "./libraries/Callback.sol"; -import "./libraries/BeaconInactivity.sol"; +import {BeaconInactivity as Inactivity} from "./libraries/BeaconInactivity.sol"; +import {BeaconAuthorization as Authorization} from "./libraries/BeaconAuthorization.sol"; import {BeaconDkg as DKG} from "./libraries/BeaconDkg.sol"; import {BeaconDkgValidator as DKGValidator} from "./BeaconDkgValidator.sol"; + import "@keep-network/sortition-pools/contracts/SortitionPool.sol"; +import "@threshold-network/solidity-contracts/contracts/staking/IApplication.sol"; +import "@threshold-network/solidity-contracts/contracts/staking/IStaking.sol"; + import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -34,29 +38,6 @@ import "@openzeppelin/contracts/utils/math/Math.sol"; // bug: https://github.com/crytic/slither/issues/1067 import {BeaconDkg} from "./libraries/BeaconDkg.sol"; -/// @title Staking contract interface -/// @notice This is an interface with just a few function signatures of the -/// Staking contract, which is available at -/// https://github.com/threshold-network/solidity-contracts/blob/main/contracts/staking/IStaking.sol -/// -/// TODO: Add a dependency to `threshold-network/solidity-contracts` and use -/// staking interface from there. -interface IRandomBeaconStaking { - function slash(uint256 amount, address[] memory operators) external; - - function seize( - uint256 amount, - uint256 rewardMultiplier, - address notifier, - address[] memory operators - ) external; - - function eligibleStake(address operator, address operatorContract) - external - view - returns (uint256); -} - /// @title Keep Random Beacon /// @notice Keep Random Beacon contract. It lets anyone request a new /// relay entry and validates the new relay entry provided by the @@ -64,7 +45,7 @@ interface IRandomBeaconStaking { /// activities such as group lifecycle or slashing. /// @dev Should be owned by the governance contract controlling Random Beacon /// parameters. -contract RandomBeacon is IRandomBeacon, Ownable { +contract RandomBeacon is IRandomBeacon, IApplication, Ownable { using SafeERC20 for IERC20; using Authorization for Authorization.Data; using BeaconDkg for DKG.Data; @@ -111,12 +92,12 @@ contract RandomBeacon is IRandomBeacon, Ownable { /// is challenged and proven to be malicious, each operator who /// signed the malicious result is slashed for /// `maliciousDkgResultSlashingAmount`. - uint256 public maliciousDkgResultSlashingAmount; + uint96 public maliciousDkgResultSlashingAmount; /// @notice Slashing amount when an unauthorized signing has been proved, /// which means the private key has been leaked and all the group /// members should be punished. - uint256 public unauthorizedSigningSlashingAmount; + uint96 public unauthorizedSigningSlashingAmount; /// @notice Duration of the sortition pool rewards ban imposed on operators /// who misbehaved during DKG by being inactive or disqualified and @@ -162,7 +143,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { SortitionPool public sortitionPool; IERC20 public tToken; - IRandomBeaconStaking public staking; + IStaking public staking; // Token bookkeeping @@ -321,6 +302,45 @@ contract RandomBeacon is IRandomBeacon, Ownable { address notifier ); + event OperatorRegistered( + address indexed stakingProvider, + address indexed operator + ); + + event AuthorizationIncreased( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount + ); + + event AuthorizationDecreaseRequested( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount, + uint64 decreasingAt + ); + + event AuthorizationDecreaseApproved(address indexed stakingProvider); + + event InvoluntaryAuthorizationDecreaseFailed( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount + ); + + event OperatorJoinedSortitionPool( + address indexed stakingProvider, + address indexed operator + ); + + event OperatorStatusUpdated( + address indexed stakingProvider, + address indexed operator + ); + /// @dev Assigns initial values to parameters to make the beacon work /// safely. These parameters are just proposed defaults and they might /// be updated with `update*` functions after the contract deployment @@ -328,7 +348,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { constructor( SortitionPool _sortitionPool, IERC20 _tToken, - IRandomBeaconStaking _staking, + IStaking _staking, DKGValidator _dkgValidator ) { sortitionPool = _sortitionPool; @@ -349,7 +369,8 @@ contract RandomBeacon is IRandomBeacon, Ownable { unauthorizedSigningNotificationRewardMultiplier = 50; dkgMaliciousResultNotificationRewardMultiplier = 100; // slither-disable-next-line too-many-digits - authorization.setMinimumAuthorization(100000 * 1e18); + authorization.setMinimumAuthorization(100000e18); // 100k T + authorization.setAuthorizationDecreaseDelay(403200); // ~10 weeks assuming 15s block time dkg.init(_sortitionPool, _dkgValidator); dkg.setResultChallengePeriodLength(11520); // ~48h assuming 15s block time @@ -365,6 +386,14 @@ contract RandomBeacon is IRandomBeacon, Ownable { groups.setGroupLifetime(403200); // ~10 weeks assuming 15s block time } + modifier onlyStakingContract() { + require( + msg.sender == address(staking), + "Caller is not the staking contract" + ); + _; + } + /// @notice Updates the values of authorization parameters. /// @dev Can be called only by the contract owner, which should be the /// random beacon governance contract. The caller is responsible for @@ -517,8 +546,8 @@ contract RandomBeacon is IRandomBeacon, Ownable { /// slashing amount function updateSlashingParameters( uint96 _relayEntrySubmissionFailureSlashingAmount, - uint256 _maliciousDkgResultSlashingAmount, - uint256 _unauthorizedSigningSlashingAmount + uint96 _maliciousDkgResultSlashingAmount, + uint96 _unauthorizedSigningSlashingAmount ) external onlyOwner { relay.setRelayEntrySubmissionFailureSlashingAmount( _relayEntrySubmissionFailureSlashingAmount @@ -541,26 +570,127 @@ contract RandomBeacon is IRandomBeacon, Ownable { sortitionPool.withdrawIneligible(recipient); } - /// @notice Registers the caller in the sortition pool. - function registerOperator() external { - address operator = msg.sender; + /// @notice Used by staking provider to set operator address that will + /// operate a node. The given staking provider can set operator + /// address only one time. The operator address can not be changed + /// and must be unique. Reverts if the operator is already set for + /// the staking provider or if the operator address is already in + /// use. Reverts if there is a pending authorization decrease for + /// the staking provider. + function registerOperator(address operator) external { + authorization.registerOperator(operator); + } - require( - !sortitionPool.isOperatorInPool(operator), - "Operator is already registered" + /// @notice Lets the operator join the sortition pool. The operator address + /// must be known - before calling this function, it has to be + /// appointed by the staking provider by calling `registerOperator`. + /// Also, the operator must have the minimum authorization required + /// by the beacon. Function reverts if there is no minimum stake + /// authorized or if the operator is not known. If there was an + /// authorization decrease requested, it is activated by starting + /// the authorization decrease delay. + function joinSortitionPool() external { + authorization.joinSortitionPool(staking, sortitionPool); + } + + /// @notice Updates status of the operator in the sortition pool. If there + /// was an authorization decrease requested, it is activated by + /// starting the authorization decrease delay. + /// Function reverts if the operator is not known. + function updateOperatorStatus(address operator) external { + authorization.updateOperatorStatus(staking, sortitionPool, operator); + } + + /// @notice Used by T staking contract to inform the beacon that the + /// authorized stake amount for the given staking provider increased. + /// + /// Reverts if the authorization amount is below the minimum. + /// + /// The function is not updating the sortition pool. Sortition pool + /// state needs to be updated by the operator with a call to + /// `joinSortitionPool` or `updateOperatorStatus`. + /// + /// @dev Can only be called by T staking contract. + function authorizationIncreased( + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) external onlyStakingContract { + authorization.authorizationIncreased( + stakingProvider, + fromAmount, + toAmount ); + } - sortitionPool.insertOperator( - operator, - staking.eligibleStake(operator, address(this)) + /// @notice Used by T staking contract to inform the beacon that the + /// authorization decrease for the given staking provider has been + /// requested. + /// + /// Reverts if the amount after deauthorization would be non-zero + /// and lower than the minimum authorization. + /// + /// If the operator is not known (`registerOperator` was not called) + /// it lets to `approveAuthorizationDecrease` immediately. If the + /// operator is known (`registerOperator` was called), the operator + /// needs to update state of the sortition pool with a call to + /// `joinSortitionPool` or `updateOperatorStatus`. After the + /// sortition pool state is in sync, authorization decrease delay + /// starts. + /// + /// After authorization decrease delay passes, authorization + /// decrease request needs to be approved with a call to + /// `approveAuthorizationDecrease` function. + /// + /// If there is a pending authorization decrease request, it is + /// overwritten. + /// + /// @dev Can only be called by T staking contract. + function authorizationDecreaseRequested( + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) external onlyStakingContract { + authorization.authorizationDecreaseRequested( + stakingProvider, + fromAmount, + toAmount ); } - /// @notice Updates the sortition pool status of the caller. - function updateOperatorStatus() external { - sortitionPool.updateOperatorStatus( - msg.sender, - staking.eligibleStake(msg.sender, address(this)) + /// @notice Approves the previously registered authorization decrease + /// request. Reverts if authorization decrease delay has not passed + /// yet or if the authorization decrease was not requested for the + /// given staking provider. + function approveAuthorizationDecrease(address stakingProvider) external { + authorization.approveAuthorizationDecrease(staking, stakingProvider); + } + + /// @notice Used by T staking contract to inform the beacon the + /// authorization has been decreased for the given staking provider + /// involuntarily, as a result of slashing. + /// + /// If the operator is not known (`registerOperator` was not called) + /// the function does nothing. The operator was never in a sortition + /// pool so there is nothing to update. + /// + /// If the operator is known, sortition pool is unlocked, and the + /// operator is in the sortition pool, the sortition pool state is + /// updated. If the sortition pool is locked, update needs to be + /// postponed. Every other staker is incentivized to call + /// `updateOperatorStatus` for the problematic operator to increase + /// their own rewards in the pool. + function involuntaryAuthorizationDecrease( + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) external onlyStakingContract { + authorization.involuntaryAuthorizationDecrease( + staking, + sortitionPool, + stakingProvider, + fromAmount, + toAmount ); } @@ -644,19 +774,21 @@ contract RandomBeacon is IRandomBeacon, Ownable { (bytes32 maliciousResultHash, uint32 maliciousSubmitter) = dkg .challengeResult(dkgResult); - uint256 slashingAmount = maliciousDkgResultSlashingAmount; + uint96 slashingAmount = maliciousDkgResultSlashingAmount; address maliciousSubmitterAddresses = sortitionPool.getIDOperator( maliciousSubmitter ); - address[] memory operatorWrapper = new address[](1); - operatorWrapper[0] = maliciousSubmitterAddresses; + address[] memory stakingProviderWrapper = new address[](1); + stakingProviderWrapper[0] = operatorToStakingProvider( + maliciousSubmitterAddresses + ); try staking.seize( slashingAmount, dkgMaliciousResultNotificationRewardMultiplier, msg.sender, - operatorWrapper + stakingProviderWrapper ) { // slither-disable-next-line reentrancy-events @@ -778,13 +910,22 @@ contract RandomBeacon is IRandomBeacon, Ownable { "Invalid group members" ); - uint256 slashingAmount = relay.submitEntry(entry, group.groupPubKey); + uint96 slashingAmount = relay.submitEntry(entry, group.groupPubKey); if (slashingAmount > 0) { address[] memory groupMembersAddresses = sortitionPool .getIDOperators(groupMembers); - try staking.slash(slashingAmount, groupMembersAddresses) { + address[] memory stakingProvidersAddresses = new address[]( + groupMembersAddresses.length + ); + for (uint256 i = 0; i < groupMembersAddresses.length; i++) { + stakingProvidersAddresses[i] = operatorToStakingProvider( + groupMembersAddresses[i] + ); + } + + try staking.slash(slashingAmount, stakingProvidersAddresses) { // slither-disable-next-line reentrancy-events emit RelayEntryDelaySlashed( currentRequestId, @@ -823,18 +964,26 @@ contract RandomBeacon is IRandomBeacon, Ownable { "Invalid group members" ); - uint256 slashingAmount = relay - .relayEntrySubmissionFailureSlashingAmount; + uint96 slashingAmount = relay.relayEntrySubmissionFailureSlashingAmount; address[] memory groupMembersAddresses = sortitionPool.getIDOperators( groupMembers ); + address[] memory stakingProvidersAddresses = new address[]( + groupMembersAddresses.length + ); + for (uint256 i = 0; i < groupMembersAddresses.length; i++) { + stakingProvidersAddresses[i] = operatorToStakingProvider( + groupMembersAddresses[i] + ); + } + try staking.seize( slashingAmount, relayEntryTimeoutNotificationRewardMultiplier, msg.sender, - groupMembersAddresses + stakingProvidersAddresses ) { // slither-disable-next-line reentrancy-events @@ -913,12 +1062,21 @@ contract RandomBeacon is IRandomBeacon, Ownable { groupMembers ); + address[] memory stakingProvidersAddresses = new address[]( + groupMembersAddresses.length + ); + for (uint256 i = 0; i < groupMembersAddresses.length; i++) { + stakingProvidersAddresses[i] = operatorToStakingProvider( + groupMembersAddresses[i] + ); + } + try staking.seize( unauthorizedSigningSlashingAmount, unauthorizedSigningNotificationRewardMultiplier, msg.sender, - groupMembersAddresses + stakingProvidersAddresses ) { // slither-disable-next-line reentrancy-events @@ -953,7 +1111,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { /// be the same as the stored one. /// @param groupMembers Identifiers of group members. function notifyOperatorInactivity( - BeaconInactivity.Claim calldata claim, + Inactivity.Claim calldata claim, uint256 nonce, uint32[] calldata groupMembers ) external { @@ -973,7 +1131,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { "Invalid group members" ); - uint32[] memory ineligibleOperators = BeaconInactivity.verifyClaim( + uint32[] memory ineligibleOperators = Inactivity.verifyClaim( sortitionPool, claim, group.groupPubKey, @@ -1015,7 +1173,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { /// execute slashing for providing a malicious DKG result or when /// a relay entry times out. function minimumAuthorization() external view returns (uint96) { - return authorization.minimumAuthorization; + return authorization.parameters.minimumAuthorization; } /// @notice Delay in seconds that needs to pass between the time @@ -1023,7 +1181,7 @@ contract RandomBeacon is IRandomBeacon, Ownable { /// gets approved. Protects against free-riders earning rewards and /// not being active in the network. function authorizationDecreaseDelay() external view returns (uint64) { - return authorization.authorizationDecreaseDelay; + return authorization.parameters.authorizationDecreaseDelay; } /// @return Flag indicating whether a relay entry request is currently @@ -1113,12 +1271,85 @@ contract RandomBeacon is IRandomBeacon, Ownable { return dkg.parameters.submitterPrecedencePeriodLength; } + /// @notice Returns the current value of the staking provider's eligible + /// stake. Eligible stake is defined as the currently authorized + /// stake minus the pending authorization decrease. Eligible stake + /// is what is used for operator's weight in the sortition pool. + /// If the authorized stake minus the pending authorization decrease + /// is below the minimum authorization, eligible stake is 0. + function eligibleStake(address stakingProvider) + external + view + returns (uint96) + { + return authorization.eligibleStake(staking, stakingProvider); + } + + /// @notice Returns the amount of stake that is pending authorization + /// decrease for the given staking provider. If no authorization + /// decrease has been requested, returns zero. + function pendingAuthorizationDecrease(address stakingProvider) + external + view + returns (uint96) + { + return authorization.pendingAuthorizationDecrease(stakingProvider); + } + + /// @notice Returns the remaining time in seconds that needs to pass before + /// the requested authorization decrease can be approved. + /// If the sortition pool state was not updated yet by the operator + /// after requesting the authorization decrease, returns + /// `type(uint64).max`. + function remainingAuthorizationDecreaseDelay(address stakingProvider) + external + view + returns (uint64) + { + return + authorization.remainingAuthorizationDecreaseDelay(stakingProvider); + } + + /// @notice Returns operator registered for the given staking provider. + function stakingProviderToOperator(address stakingProvider) + external + view + returns (address) + { + return authorization.stakingProviderToOperator[stakingProvider]; + } + + /// @notice Returns staking provider of the given operator. + function operatorToStakingProvider(address operator) + public + view + returns (address) + { + return authorization.operatorToStakingProvider[operator]; + } + + /// @notice Checks if the operator's authorized stake is in sync with + /// operator's weight in the sortition pool. + /// If the operator is not in the sortition pool and their + /// authorized stake is non-zero, function returns false. + function isOperatorUpToDate(address operator) external view returns (bool) { + return + authorization.isOperatorUpToDate(staking, sortitionPool, operator); + } + + /// @notice Returns true if the given operator is in the sortition pool. + /// Otherwise, returns false. + function isOperatorInPool(address operator) external view returns (bool) { + return sortitionPool.isOperatorInPool(operator); + } + /// @notice Selects a new group of operators based on the provided seed. /// At least one operator has to be registered in the pool, /// otherwise the function fails reverting the transaction. /// @param seed Number used to select operators to the group. /// @return IDs of selected group members. function selectGroup(bytes32 seed) external view returns (uint32[] memory) { + // TODO: Read seed from RandomBeaconDkg return sortitionPool.selectGroup(DKG.groupSize, seed); } } diff --git a/solidity/random-beacon/contracts/RandomBeaconGovernance.sol b/solidity/random-beacon/contracts/RandomBeaconGovernance.sol index ff263ce6fe..ec2c58da19 100644 --- a/solidity/random-beacon/contracts/RandomBeaconGovernance.sol +++ b/solidity/random-beacon/contracts/RandomBeaconGovernance.sol @@ -67,10 +67,10 @@ contract RandomBeaconGovernance is Ownable { uint96 public newRelayEntrySubmissionFailureSlashingAmount; uint256 public relayEntrySubmissionFailureSlashingAmountChangeInitiated; - uint256 public newMaliciousDkgResultSlashingAmount; + uint96 public newMaliciousDkgResultSlashingAmount; uint256 public maliciousDkgResultSlashingAmountChangeInitiated; - uint256 public newUnauthorizedSigningSlashingAmount; + uint96 public newUnauthorizedSigningSlashingAmount; uint256 public unauthorizedSigningSlashingAmountChangeInitiated; uint256 public newSortitionPoolRewardsBanDuration; @@ -187,7 +187,7 @@ contract RandomBeaconGovernance is Ownable { ); event RelayEntrySubmissionFailureSlashingAmountUpdateStarted( - uint256 relayEntrySubmissionFailureSlashingAmount, + uint96 relayEntrySubmissionFailureSlashingAmount, uint256 timestamp ); event RelayEntrySubmissionFailureSlashingAmountUpdated( @@ -195,19 +195,19 @@ contract RandomBeaconGovernance is Ownable { ); event MaliciousDkgResultSlashingAmountUpdateStarted( - uint256 maliciousDkgResultSlashingAmount, + uint96 maliciousDkgResultSlashingAmount, uint256 timestamp ); event MaliciousDkgResultSlashingAmountUpdated( - uint256 maliciousDkgResultSlashingAmount + uint96 maliciousDkgResultSlashingAmount ); event UnauthorizedSigningSlashingAmountUpdateStarted( - uint256 unauthorizedSigningSlashingAmount, + uint96 unauthorizedSigningSlashingAmount, uint256 timestamp ); event UnauthorizedSigningSlashingAmountUpdated( - uint256 unauthorizedSigningSlashingAmount + uint96 unauthorizedSigningSlashingAmount ); event SortitionPoolRewardsBanDurationUpdateStarted( @@ -1062,7 +1062,7 @@ contract RandomBeaconGovernance is Ownable { /// @param _newMaliciousDkgResultSlashingAmount New malicious DKG result /// slashing amount function beginMaliciousDkgResultSlashingAmountUpdate( - uint256 _newMaliciousDkgResultSlashingAmount + uint96 _newMaliciousDkgResultSlashingAmount ) external onlyOwner { /* solhint-disable not-rely-on-time */ newMaliciousDkgResultSlashingAmount = _newMaliciousDkgResultSlashingAmount; @@ -1103,7 +1103,7 @@ contract RandomBeaconGovernance is Ownable { /// @param _newUnauthorizedSigningSlashingAmount New unauthorized signing /// slashing amount function beginUnauthorizedSigningSlashingAmountUpdate( - uint256 _newUnauthorizedSigningSlashingAmount + uint96 _newUnauthorizedSigningSlashingAmount ) external onlyOwner { /* solhint-disable not-rely-on-time */ newUnauthorizedSigningSlashingAmount = _newUnauthorizedSigningSlashingAmount; diff --git a/solidity/random-beacon/contracts/libraries/Authorization.sol b/solidity/random-beacon/contracts/libraries/Authorization.sol deleted file mode 100644 index d42f2da437..0000000000 --- a/solidity/random-beacon/contracts/libraries/Authorization.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄ -// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ -// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ -// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ -// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ -// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀ -// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ -// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ -// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ -// -// - -pragma solidity ^0.8.9; - -library Authorization { - struct Data { - uint96 minimumAuthorization; - uint64 authorizationDecreaseDelay; - } - - function setMinimumAuthorization( - Data storage self, - uint96 _minimumAuthorization - ) internal { - self.minimumAuthorization = _minimumAuthorization; - } - - function setAuthorizationDecreaseDelay( - Data storage self, - uint64 _authorizationDecreaseDelay - ) internal { - self.authorizationDecreaseDelay = _authorizationDecreaseDelay; - } -} diff --git a/solidity/random-beacon/contracts/libraries/BeaconAuthorization.sol b/solidity/random-beacon/contracts/libraries/BeaconAuthorization.sol new file mode 100644 index 0000000000..425085aa1e --- /dev/null +++ b/solidity/random-beacon/contracts/libraries/BeaconAuthorization.sol @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: MIT +// +// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄ +// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ +// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ +// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀ +// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ +// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ +// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ +// +// + +// Initial version copied from Keep Network ECDSA Wallets: +// https://github.com/keep-network/keep-core/blob/5286ebc8ff99b2aa6569f9dd97fd6995f25b4630/solidity/ecdsa/contracts/libraries/EcdsaAuthorization.sol +// +// With the following differences: +// - functions' visibility was changed to public/external to deploy it as a linked +// library. +// - documentation was updated to be more generic. + +pragma solidity ^0.8.9; + +import "@keep-network/sortition-pools/contracts/SortitionPool.sol"; +import "@threshold-network/solidity-contracts/contracts/staking/IStaking.sol"; + +/// @notice Library managing the state of stake authorizations for the operator +/// contract and the presence of operators in the sortition +/// pool based on the stake authorized for them. +library BeaconAuthorization { + struct Parameters { + // The minimum authorization required by the beacon so that + // operator can join the sortition pool and do the work. + uint96 minimumAuthorization; + // Authorization decrease delay in seconds between the time + // authorization decrease is requested and the time the authorization + // decrease can be approved. It is always the same value, no matter if + // authorization decrease amount is small, significant, or if it is + // a decrease to zero. + uint64 authorizationDecreaseDelay; + } + + struct AuthorizationDecrease { + uint96 decreasingBy; // amount + uint64 decreasingAt; // timestamp + } + + struct Data { + Parameters parameters; + mapping(address => address) stakingProviderToOperator; + mapping(address => address) operatorToStakingProvider; + mapping(address => AuthorizationDecrease) pendingDecreases; + } + + event OperatorRegistered( + address indexed stakingProvider, + address indexed operator + ); + + event AuthorizationIncreased( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount + ); + + event AuthorizationDecreaseRequested( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount, + uint64 decreasingAt + ); + + event AuthorizationDecreaseApproved(address indexed stakingProvider); + + event InvoluntaryAuthorizationDecreaseFailed( + address indexed stakingProvider, + address indexed operator, + uint96 fromAmount, + uint96 toAmount + ); + + event OperatorJoinedSortitionPool( + address indexed stakingProvider, + address indexed operator + ); + + event OperatorStatusUpdated( + address indexed stakingProvider, + address indexed operator + ); + + /// @notice Sets the minimum authorization for the beacon. Without + /// at least the minimum authorization, staking provider is not + /// eligible to join and operate in the network. + function setMinimumAuthorization( + Data storage self, + uint96 _minimumAuthorization + ) external { + self.parameters.minimumAuthorization = _minimumAuthorization; + } + + /// @notice Sets the authorization decrease delay. It is the time in seconds + /// that needs to pass between the time authorization decrease is + /// requested and the time the authorization decrease can be + /// approved, no matter the authorization decrease amount. + function setAuthorizationDecreaseDelay( + Data storage self, + uint64 _authorizationDecreaseDelay + ) external { + self + .parameters + .authorizationDecreaseDelay = _authorizationDecreaseDelay; + } + + /// @notice Used by staking provider to set operator address that will + /// operate a node. The given staking provider can set operator + /// address only one time. The operator address can not be changed + /// and must be unique. Reverts if the operator is already set for + /// the staking provider or if the operator address is already in + /// use. Reverts if there is a pending authorization decrease for + /// the staking provider. + function registerOperator(Data storage self, address operator) public { + address stakingProvider = msg.sender; + + require(operator != address(0), "Operator can not be zero address"); + require( + self.stakingProviderToOperator[stakingProvider] == address(0), + "Operator already set for the staking provider" + ); + require( + self.operatorToStakingProvider[operator] == address(0), + "Operator address already in use" + ); + + // Authorization request for a staking provider who has not yet + // registered their operator can be approved immediately. + // We need to make sure that the approval happens before operator + // is registered to do not let the operator join the sortition pool + // with an unresolved authorization decrease request that can be + // approved at any point. + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + require( + decrease.decreasingAt == 0, + "There is a pending authorization decrease request" + ); + + emit OperatorRegistered(stakingProvider, operator); + + self.stakingProviderToOperator[stakingProvider] = operator; + self.operatorToStakingProvider[operator] = stakingProvider; + } + + /// @notice Used by T staking contract to inform the beacon that the + /// authorized stake amount for the given staking provider increased. + /// + /// Reverts if the authorization amount is below the minimum. + /// + /// The function is not updating the sortition pool. Sortition pool + /// state needs to be updated by the operator with a call to + /// `joinSortitionPool` or `updateOperatorStatus`. + /// + /// @dev Should only be callable by T staking contract. + function authorizationIncreased( + Data storage self, + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) public { + require( + toAmount >= self.parameters.minimumAuthorization, + "Authorization below the minimum" + ); + + // Note that this function does not require the operator address to be + // set for the given staking provider. This allows the stake owner + // who is also an authorizer to increase the authorization before the + // staking provider sets the operator. This allows delegating stake + // and increasing authorization immediately one after another without + // having to wait for the staking provider to do their part. + + address operator = self.stakingProviderToOperator[stakingProvider]; + emit AuthorizationIncreased( + stakingProvider, + operator, + fromAmount, + toAmount + ); + } + + /// @notice Used by T staking contract to inform the beacon that the + /// authorization decrease for the given staking provider has been + /// requested. + /// + /// Reverts if the amount after deauthorization would be non-zero + /// and lower than the minimum authorization. + /// + /// If the operator is not known (`registerOperator` was not called) + /// it lets to `approveAuthorizationDecrease` immediately. If the + /// operator is known (`registerOperator` was called), the operator + /// needs to update state of the sortition pool with a call to + /// `joinSortitionPool` or `updateOperatorStatus`. After the + /// sortition pool state is in sync, authorization decrease delay + /// starts. + /// + /// After authorization decrease delay passes, authorization + /// decrease request needs to be approved with a call to + /// `approveAuthorizationDecrease` function. + /// + /// If there is a pending authorization decrease request, it is + /// overwritten. + /// + /// @dev Should only be callable by T staking contract. + function authorizationDecreaseRequested( + Data storage self, + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) public { + require( + toAmount == 0 || toAmount >= self.parameters.minimumAuthorization, + "Authorization amount should be 0 or above the minimum" + ); + + address operator = self.stakingProviderToOperator[stakingProvider]; + + uint64 decreasingAt; + + if (operator == address(0)) { + // Operator is not known. It means `registerOperator` was not + // called yet, and there is no chance the operator could + // call `joinSortitionPool`. We can let to approve authorization + // decrease immediately because that operator was never in the + // sortition pool. + + // solhint-disable-next-line not-rely-on-time + decreasingAt = uint64(block.timestamp); + } else { + // Operator is known. It means that this operator is or was in + // the sortition pool. Before authorization decrease delay starts, + // the operator needs to update the state of the sortition pool + // with a call to `joinSortitionPool` or `updateOperatorStatus`. + // For now, we set `decreasingAt` as "never decreasing" and let + // it be updated by `joinSortitionPool` or `updateOperatorStatus` + // once we know the sortition pool is in sync. + + // solhint-disable-next-line not-rely-on-time + decreasingAt = type(uint64).max; + } + + uint96 decreasingBy = fromAmount - toAmount; + + self.pendingDecreases[stakingProvider] = AuthorizationDecrease( + decreasingBy, + decreasingAt + ); + + emit AuthorizationDecreaseRequested( + stakingProvider, + operator, + fromAmount, + toAmount, + decreasingAt + ); + } + + /// @notice Approves the previously registered authorization decrease + /// request. Reverts if authorization decrease delay have not passed + /// yet or if the authorization decrease was not requested for the + /// given staking provider. + function approveAuthorizationDecrease( + Data storage self, + IStaking tokenStaking, + address stakingProvider + ) public { + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + require( + decrease.decreasingAt > 0, + "Authorization decrease not requested" + ); + require( + decrease.decreasingAt != type(uint64).max, + "Authorization decrease request not activated" + ); + require( + // solhint-disable-next-line not-rely-on-time + block.timestamp >= decrease.decreasingAt, + "Authorization decrease delay not passed" + ); + + emit AuthorizationDecreaseApproved(stakingProvider); + + // slither-disable-next-line unused-return + tokenStaking.approveAuthorizationDecrease(stakingProvider); + delete self.pendingDecreases[stakingProvider]; + } + + /// @notice Used by T staking contract to inform the beacon the + /// authorization has been decreased for the given staking provider + /// involuntarily, as a result of slashing. + /// + /// If the operator is not known (`registerOperator` was not called) + /// the function does nothing. The operator was never in a sortition + /// pool so there is nothing to update. + /// + /// If the operator is known, sortition pool is unlocked, and the + /// operator is in the sortition pool, the sortition pool state is + /// updated. If the sortition pool is locked, update needs to be + /// postponed. Every other staker is incentivized to call + /// `updateOperatorStatus` for the problematic operator to increase + /// their own rewards in the pool. + /// + /// @dev Should only be callable by T staking contract. + function involuntaryAuthorizationDecrease( + Data storage self, + IStaking tokenStaking, + SortitionPool sortitionPool, + address stakingProvider, + uint96 fromAmount, + uint96 toAmount + ) public { + address operator = self.stakingProviderToOperator[stakingProvider]; + + if (operator == address(0)) { + // Operator is not known. It means `registerOperator` was not + // called yet, and there is no chance the operator could + // call `joinSortitionPool`. We can just ignore this update because + // operator was never in the sortition pool. + return; + } else { + // Operator is known. It means that this operator is or was in the + // sortition pool and the sortition pool may need to be updated. + // + // If the sortition pool is not locked and the operator is in the + // sortition pool, we are updating it. + // + // To keep stakes synchronized between applications when staking + // providers are slashed, without the risk of running out of gas, + // the staking contract queues up slashings and let users process + // the transactions. When an application slashes one or more staking + // providers, it adds them to the slashing queue on the staking + // contract. A queue entry contains the staking provider’s address + // and the amount they are due to be slashed. + // + // When there is at least one staking provider in the slashing + // queue, any account can submit a transaction processing one or + // more staking providers' slashings, and collecting a reward for + // doing so. A queued slashing is processed by updating the staking + // provider’s stake to the post-slashing amount, updating authorized + // amount for each affected application, and notifying all affected + // applications that the staking provider’s authorized stake has + // been reduced due to slashing. + // + // The entire idea is that the process transaction is expensive + // because each application needs to be updated, so the reward for + // the processor is hefty and comes from the slashed tokens. + // Practically, it means that if the sortition pool is unlocked, and + // can be updated, it should be updated because we already paid + // someone for updating it. + // + // If the sortition pool is locked, update needs to wait. Other + // sortition pool members are incentivized to call + // `updateOperatorStatus` for the problematic operator because they + // will increase their rewards this way. + if (sortitionPool.isOperatorInPool(operator)) { + if (sortitionPool.isLocked()) { + emit InvoluntaryAuthorizationDecreaseFailed( + stakingProvider, + operator, + fromAmount, + toAmount + ); + } else { + updateOperatorStatus( + self, + tokenStaking, + sortitionPool, + operator + ); + } + } + } + } + + /// @notice Lets the operator join the sortition pool. The operator address + /// must be known - before calling this function, it has to be + /// appointed by the staking provider by calling `registerOperator`. + /// Also, the operator must have the minimum authorization required + /// by the beacon. Function reverts if there is no minimum stake + /// authorized or if the operator is not known. If there was an + /// authorization decrease requested, it is activated by starting + /// the authorization decrease delay. + function joinSortitionPool( + Data storage self, + IStaking tokenStaking, + SortitionPool sortitionPool + ) public { + address operator = msg.sender; + + address stakingProvider = self.operatorToStakingProvider[operator]; + require(stakingProvider != address(0), "Unknown operator"); + + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + + uint96 _eligibleStake = eligibleStake( + self, + tokenStaking, + stakingProvider, + decrease.decreasingBy + ); + + require(_eligibleStake != 0, "Authorization below the minimum"); + + emit OperatorJoinedSortitionPool(stakingProvider, operator); + + sortitionPool.insertOperator(operator, _eligibleStake); + + // If there is a pending authorization decrease request, activate it. + // At this point, the sortition pool state is up to date so the + // authorization decrease delay can start counting. + if (decrease.decreasingAt == type(uint64).max) { + decrease.decreasingAt = + // solhint-disable-next-line not-rely-on-time + uint64(block.timestamp) + + self.parameters.authorizationDecreaseDelay; + } + } + + /// @notice Updates status of the operator in the sortition pool. If there + /// was an authorization decrease requested, it is activated by + /// starting the authorization decrease delay. + /// Function reverts if the operator is not known. + function updateOperatorStatus( + Data storage self, + IStaking tokenStaking, + SortitionPool sortitionPool, + address operator + ) public { + address stakingProvider = self.operatorToStakingProvider[operator]; + require(stakingProvider != address(0), "Unknown operator"); + + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + + emit OperatorStatusUpdated(stakingProvider, operator); + + if (sortitionPool.isOperatorInPool(operator)) { + uint96 _eligibleStake = eligibleStake( + self, + tokenStaking, + stakingProvider, + decrease.decreasingBy + ); + + sortitionPool.updateOperatorStatus(operator, _eligibleStake); + } + + // If there is a pending authorization decrease request, activate it. + // At this point, the sortition pool state is up to date so the + // authorization decrease delay can start counting. + if (decrease.decreasingAt == type(uint64).max) { + decrease.decreasingAt = + // solhint-disable-next-line not-rely-on-time + uint64(block.timestamp) + + self.parameters.authorizationDecreaseDelay; + } + } + + /// @notice Checks if the operator's authorized stake is in sync with + /// operator's weight in the sortition pool. + /// If the operator is not in the sortition pool and their + /// authorized stake is non-zero, function returns false. + function isOperatorUpToDate( + Data storage self, + IStaking tokenStaking, + SortitionPool sortitionPool, + address operator + ) public view returns (bool) { + address stakingProvider = self.operatorToStakingProvider[operator]; + require(stakingProvider != address(0), "Unknown operator"); + + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + + uint96 _eligibleStake = eligibleStake( + self, + tokenStaking, + stakingProvider, + decrease.decreasingBy + ); + + if (!sortitionPool.isOperatorInPool(operator)) { + return _eligibleStake == 0; + } else { + return sortitionPool.isOperatorUpToDate(operator, _eligibleStake); + } + } + + /// @notice Returns the current value of the staking provider's eligible + /// stake. Eligible stake is defined as the currently authorized + /// stake minus the pending authorization decrease. Eligible stake + /// is what is used for operator's weight in the pool. If the + /// authorized stake minus the pending authorization decrease is + /// below the minimum authorization, eligible stake is 0. + /// @dev This function can be exposed to the public in contrast to the + /// second variant accepting `decreasingBy` as a parameter. + function eligibleStake( + Data storage self, + IStaking tokenStaking, + address stakingProvider + ) public view returns (uint96) { + return + eligibleStake( + self, + tokenStaking, + stakingProvider, + pendingAuthorizationDecrease(self, stakingProvider) + ); + } + + /// @notice Returns the current value of the staking provider's eligible + /// stake. Eligible stake is defined as the currently authorized + /// stake minus the pending authorization decrease. Eligible stake + /// is what is used for operator's weight in the pool. If the + /// authorized stake minus the pending authorization decrease is + /// below the minimum authorization, eligible stake is 0. + /// @dev This function is not intended to be exposes to the public. + /// `decreasingBy` must be fetched from `pendingDecreases` mapping and + /// it is passed as a parameter to optimize gas usage of functions that + /// call `eligibleStake` and need to use `AuthorizationDecrease` + /// fetched from `pendingDecreases` for some additional logic. + function eligibleStake( + Data storage self, + IStaking tokenStaking, + address stakingProvider, + uint96 decreasingBy + ) public view returns (uint96) { + uint96 authorizedStake = tokenStaking.authorizedStake( + stakingProvider, + address(this) + ); + + uint96 _eligibleStake = authorizedStake > decreasingBy + ? authorizedStake - decreasingBy + : 0; + + if (_eligibleStake < self.parameters.minimumAuthorization) { + return 0; + } else { + return _eligibleStake; + } + } + + /// @notice Returns the amount of stake that is pending authorization + /// decrease for the given staking provider. If no authorization + /// decrease has been requested, returns zero. + function pendingAuthorizationDecrease( + Data storage self, + address stakingProvider + ) public view returns (uint96) { + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + + return decrease.decreasingBy; + } + + /// @notice Returns the remaining time in seconds that needs to pass before + /// the requested authorization decrease can be approved. + /// If the sortition pool state was not updated yet by the operator + /// after requesting the authorization decrease, returns + /// `type(uint64).max`. + function remainingAuthorizationDecreaseDelay( + Data storage self, + address stakingProvider + ) external view returns (uint64) { + AuthorizationDecrease storage decrease = self.pendingDecreases[ + stakingProvider + ]; + + if (decrease.decreasingAt == type(uint64).max) { + return type(uint64).max; + } + + // solhint-disable-next-line not-rely-on-time + uint64 _now = uint64(block.timestamp); + return _now > decrease.decreasingAt ? 0 : decrease.decreasingAt - _now; + } +} diff --git a/solidity/random-beacon/contracts/test/RandomBeaconStub.sol b/solidity/random-beacon/contracts/test/RandomBeaconStub.sol index 2612bd7f8f..e7d6a29896 100644 --- a/solidity/random-beacon/contracts/test/RandomBeaconStub.sol +++ b/solidity/random-beacon/contracts/test/RandomBeaconStub.sol @@ -15,7 +15,7 @@ contract RandomBeaconStub is RandomBeacon { constructor( SortitionPool _sortitionPool, IERC20 _tToken, - IRandomBeaconStaking _staking, + IStaking _staking, DKGValidator _dkgValidator ) RandomBeacon(_sortitionPool, _tToken, _staking, _dkgValidator) {} diff --git a/solidity/random-beacon/contracts/test/StakingStub.sol b/solidity/random-beacon/contracts/test/StakingStub.sol deleted file mode 100644 index 57f2083848..0000000000 --- a/solidity/random-beacon/contracts/test/StakingStub.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.6; - -import "../RandomBeacon.sol"; - -// TODO: get rid of this contract; use T staking contract for tests -// Stub contract used in tests -contract StakingStub is IRandomBeaconStaking { - mapping(address => uint256) public stakedTokens; - - event Slashed(uint256 amount, address[] operators); - - event Seized( - uint256 amount, - uint256 rewardMultiplier, - address notifier, - address[] operators - ); - - function slash(uint256 amount, address[] memory operators) - external - override - { - if (amount > 0 && operators.length > 0) { - emit Slashed(amount, operators); - } - } - - function seize( - uint256 amount, - uint256 rewardMultiplier, - address notifier, - address[] memory operators - ) external override { - if (amount > 0 && operators.length > 0) { - emit Seized(amount, rewardMultiplier, notifier, operators); - } - } - - function rolesOf(address operator) - external - view - returns ( - address owner, - address beneficiary, - address authorizer - ) - { - return (operator, operator, operator); - } - - function eligibleStake( - address operator, - address // operatorContract - ) external view override returns (uint256) { - return stakedTokens[operator]; - } - - function setStake(address operator, uint256 stake) public { - stakedTokens[operator] = stake; - } -} diff --git a/solidity/random-beacon/contracts/test/TestToken.sol b/solidity/random-beacon/contracts/test/TestToken.sol deleted file mode 100644 index 2b593f73c6..0000000000 --- a/solidity/random-beacon/contracts/test/TestToken.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol"; - -contract TestToken is ERC20WithPermit { - /* solhint-disable-next-line no-empty-blocks */ - constructor() ERC20WithPermit("Test Token", "TT") {} -} diff --git a/solidity/random-beacon/hardhat.config.ts b/solidity/random-beacon/hardhat.config.ts index 1d45659418..b2dd2e5c19 100644 --- a/solidity/random-beacon/hardhat.config.ts +++ b/solidity/random-beacon/hardhat.config.ts @@ -8,6 +8,25 @@ import "@nomiclabs/hardhat-waffle" import "hardhat-gas-reporter" import "hardhat-contract-sizer" import "@typechain/hardhat" +import "hardhat-dependency-compiler" + +import { task } from "hardhat/config" +import { TASK_TEST } from "hardhat/builtin-tasks/task-names" + +// Configuration for testing environment. +export const testConfig = { + // How many accounts we expect to define for non-staking related signers, e.g. + // deployer, thirdParty, governance. + // It is used as an offset for getting accounts for operators and stakes registration. + nonStakingAccountsCount: 10, + + // How many roles do we need to define for staking, i.e. stakeOwner, stakingProvider, + // operator, beneficiary, authorizer. + stakingRolesCount: 5, + + // Number of operators to register. Should be at least the same as group size. + operatorsCount: 64, +} const config: HardhatUserConfig = { solidity: { @@ -37,8 +56,14 @@ const config: HardhatUserConfig = { ? parseInt(process.env.FORKING_BLOCK, 10) : undefined, }, - accounts: { count: 70 }, + accounts: { + // Number of accounts that should be predefined on the testing environment. + count: + testConfig.nonStakingAccountsCount + + testConfig.stakingRolesCount * testConfig.operatorsCount, + }, tags: ["local"], + allowUnlimitedContractSize: true, }, ropsten: { url: process.env.CHAIN_API_URL || "", @@ -60,11 +85,39 @@ const config: HardhatUserConfig = { default: 0, // take the first account as deployer }, }, + external: { + contracts: [ + { + artifacts: + "node_modules/@threshold-network/solidity-contracts/export/artifacts", + deploy: + "node_modules/@threshold-network/solidity-contracts/export/deploy", + }, + ], + // deployments: { + // // For hardhat environment we can fork the mainnet, so we need to point it + // // to the contract artifacts. + // hardhat: process.env.FORKING_URL ? ["./external/mainnet"] : [], + // // For development environment we expect the local dependencies to be linked + // // with `yarn link` command. + // development: ["node_modules/@keep-network/keep-core/artifacts"], + // ropsten: ["node_modules/@keep-network/keep-core/artifacts"], + // mainnet: ["./external/mainnet"], + // }, + }, + dependencyCompiler: { + paths: [ + "@threshold-network/solidity-contracts/contracts/token/T.sol", + "@threshold-network/solidity-contracts/contracts/staking/TokenStaking.sol", + ], + keep: true, + }, contractSizer: { alphaSort: true, disambiguatePaths: false, runOnCompile: true, strict: true, + except: ["^contracts/test", "TokenStaking$"], }, mocha: { timeout: 60000, @@ -74,4 +127,19 @@ const config: HardhatUserConfig = { }, } +task(TASK_TEST, "Runs mocha tests").setAction(async (args, hre, runSuper) => { + // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require + const { constants } = require("./test/fixtures") + + if (testConfig.operatorsCount < constants.groupSize) { + throw new Error( + "not enough accounts predefined for configured group size: " + + `expected group size: ${constants.groupSize} ` + + `number of predefined accounts: ${testConfig.operatorsCount}` + ) + } + + return runSuper(args) +}) + export default config diff --git a/solidity/random-beacon/package.json b/solidity/random-beacon/package.json index fefd98a9d4..cb042a5af0 100644 --- a/solidity/random-beacon/package.json +++ b/solidity/random-beacon/package.json @@ -30,15 +30,16 @@ "prepack": "tsc -p tsconfig.export.json && hardhat export-artifacts export/artifacts" }, "dependencies": { - "@keep-network/sortition-pools": "1.2.0-dev.24", + "@keep-network/sortition-pools": "^2.0.0-pre.6", "@openzeppelin/contracts": "^4.4.2", - "@thesis/solidity-contracts": "github:thesis/solidity-contracts#4985bcf" + "@thesis/solidity-contracts": "github:thesis/solidity-contracts#4985bcf", + "@threshold-network/solidity-contracts": ">1.1.0-dev <1.1.0-ropsten" }, "devDependencies": { "@defi-wonderland/smock": "^2.0.7", "@keep-network/hardhat-helpers": "0.4.1-pre.1", "@keep-network/hardhat-local-networks-config": "^0.1.0-pre.0", - "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", "@nomiclabs/hardhat-waffle": "^2.0.1", "@tenderly/hardhat-tenderly": "^1.0.12", "@thesis-co/eslint-config": "github:thesis/eslint-config", @@ -52,8 +53,9 @@ "ethereum-waffle": "^3.4.0", "ethers": "^5.4.7", "hardhat": "^2.6.4", - "hardhat-contract-sizer": "^2.1.1", - "hardhat-deploy": "^0.9.1", + "hardhat-contract-sizer": "^2.5.1", + "hardhat-dependency-compiler": "^1.1.2", + "hardhat-deploy": "^0.11.2", "hardhat-gas-reporter": "^1.0.4", "prettier": "^2.4.1", "prettier-plugin-solidity": "^1.0.0-beta.18", diff --git a/solidity/random-beacon/test/AltBn128.test.ts b/solidity/random-beacon/test/AltBn128.test.ts index 5fa8c09a9e..7fe7666301 100644 --- a/solidity/random-beacon/test/AltBn128.test.ts +++ b/solidity/random-beacon/test/AltBn128.test.ts @@ -1,7 +1,7 @@ import { ethers, waffle } from "hardhat" import { expect } from "chai" -import type { TestAltBn128 } from "../typechain" +import type { TestAltBn128, TestAltBn128__factory } from "../typechain" describe("AltBn128", () => { const g1 = @@ -14,7 +14,9 @@ describe("AltBn128", () => { let testAltBn128: TestAltBn128 const fixture = async () => { - const TestAltBn128 = await ethers.getContractFactory("TestAltBn128") + const TestAltBn128 = await ethers.getContractFactory( + "TestAltBn128" + ) testAltBn128 = await TestAltBn128.deploy() await testAltBn128.deployed() diff --git a/solidity/random-beacon/test/BeaconDkgValidator.test.ts b/solidity/random-beacon/test/BeaconDkgValidator.test.ts index b17bde54ce..f5c669ce26 100644 --- a/solidity/random-beacon/test/BeaconDkgValidator.test.ts +++ b/solidity/random-beacon/test/BeaconDkgValidator.test.ts @@ -1,7 +1,13 @@ /* eslint-disable @typescript-eslint/no-unused-expressions, no-await-in-loop */ import { BigNumber } from "ethers" -import { ethers, helpers, getUnnamedAccounts, waffle } from "hardhat" +import { + ethers, + helpers, + getUnnamedAccounts, + waffle, + deployments, +} from "hardhat" import { expect } from "chai" import blsData from "./data/bls" @@ -14,24 +20,22 @@ import type { SortitionPool, BeaconDkgValidator as DKGValidator, BeaconDkg as DKG, + TokenStaking, + T, } from "../typechain" const { createSnapshot, restoreSnapshot } = helpers.snapshot const { to1e18 } = helpers.number const fixture = async () => { - const TestToken = await ethers.getContractFactory("TestToken") - const testToken = await TestToken.deploy() - await testToken.deployed() - - const StakingStub = await ethers.getContractFactory("StakingStub") - const stakingStub = await StakingStub.deploy() - await stakingStub.deployed() + await deployments.fixture(["TokenStaking"]) + const t: T = await ethers.getContract("T") + const staking: TokenStaking = await ethers.getContract("TokenStaking") const SortitionPool = await ethers.getContractFactory("SortitionPool") const sortitionPool = (await SortitionPool.deploy( - stakingStub.address, - testToken.address, + staking.address, + t.address, constants.poolWeightDivisor )) as SortitionPool @@ -681,8 +685,7 @@ describe("BeaconDkgValidator", () => { ) const signatures = [] for (let i = 0; i < signingOperators.length; i++) { - const { address } = signingOperators[i] - const ethersSigner = await ethers.getSigner(address) + const { signer: ethersSigner } = signingOperators[i] const signature = await ethersSigner.signMessage( ethers.utils.arrayify(wrongResultHash) ) diff --git a/solidity/random-beacon/test/Groups.Expiration.test.ts b/solidity/random-beacon/test/Groups.Expiration.test.ts index b66c0c5c5f..3507f1563f 100644 --- a/solidity/random-beacon/test/Groups.Expiration.test.ts +++ b/solidity/random-beacon/test/Groups.Expiration.test.ts @@ -5,10 +5,12 @@ import { expect } from "chai" import { noMisbehaved, hashDKGMembers } from "./utils/dkg" -import type { GroupsStub } from "../typechain" +import type { GroupsStub, GroupsStub__factory } from "../typechain" const fixture = async () => { - const GroupsStub = await ethers.getContractFactory("GroupsStub") + const GroupsStub = await ethers.getContractFactory( + "GroupsStub" + ) const groups = await GroupsStub.deploy() return groups diff --git a/solidity/random-beacon/test/Groups.Termination.test.ts b/solidity/random-beacon/test/Groups.Termination.test.ts index f7ca26a55c..4eff341030 100644 --- a/solidity/random-beacon/test/Groups.Termination.test.ts +++ b/solidity/random-beacon/test/Groups.Termination.test.ts @@ -5,10 +5,12 @@ import { expect } from "chai" import { noMisbehaved, hashDKGMembers } from "./utils/dkg" -import type { GroupsStub } from "../typechain" +import type { GroupsStub, GroupsStub__factory } from "../typechain" const fixture = async () => { - const GroupsStub = await ethers.getContractFactory("GroupsStub") + const GroupsStub = await ethers.getContractFactory( + "GroupsStub" + ) const groups = await GroupsStub.deploy() return groups diff --git a/solidity/random-beacon/test/Groups.test.ts b/solidity/random-beacon/test/Groups.test.ts index 6ba769f368..b564609fb0 100644 --- a/solidity/random-beacon/test/Groups.test.ts +++ b/solidity/random-beacon/test/Groups.test.ts @@ -8,14 +8,16 @@ import { constants } from "./fixtures" import { noMisbehaved, hashDKGMembers } from "./utils/dkg" import { hashUint32Array } from "./utils/groups" -import type { GroupsStub } from "../typechain" +import type { GroupsStub, GroupsStub__factory } from "../typechain" import type { ContractTransaction } from "ethers" import type { Groups } from "../typechain/GroupsStub" const { keccak256 } = ethers.utils const fixture = async () => { - const GroupsStub = await ethers.getContractFactory("GroupsStub") + const GroupsStub = await ethers.getContractFactory( + "GroupsStub" + ) const groups = await GroupsStub.deploy() return groups diff --git a/solidity/random-beacon/test/ModUtils.test.ts b/solidity/random-beacon/test/ModUtils.test.ts index 20a6d68eba..d371cac540 100644 --- a/solidity/random-beacon/test/ModUtils.test.ts +++ b/solidity/random-beacon/test/ModUtils.test.ts @@ -1,12 +1,14 @@ import { ethers, waffle } from "hardhat" -import type { TestModUtils } from "../typechain" +import type { TestModUtils, TestModUtils__factory } from "../typechain" describe("ModUtils", () => { let testModUtils: TestModUtils const fixture = async () => { - const TestModUtils = await ethers.getContractFactory("TestModUtils") + const TestModUtils = await ethers.getContractFactory( + "TestModUtils" + ) testModUtils = await TestModUtils.deploy() await testModUtils.deployed() diff --git a/solidity/random-beacon/test/RandomBeacon.Authorization.test.ts b/solidity/random-beacon/test/RandomBeacon.Authorization.test.ts new file mode 100644 index 0000000000..b2490b900a --- /dev/null +++ b/solidity/random-beacon/test/RandomBeacon.Authorization.test.ts @@ -0,0 +1,2949 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +import { ethers, helpers } from "hardhat" +import { smock } from "@defi-wonderland/smock" +import { expect } from "chai" +import { to1e18 } from "@keep-network/hardhat-helpers/dist/src/number" + +import { constants, params, testDeployment } from "./fixtures" + +import type { FakeContract } from "@defi-wonderland/smock" +import type { BigNumber, ContractTransaction } from "ethers" +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" +import type { + RandomBeacon, + SortitionPool, + TokenStaking, + T, + IApplication, +} from "../typechain" + +const { mineBlocks } = helpers.time + +const { createSnapshot, restoreSnapshot } = helpers.snapshot + +const ZERO_ADDRESS = ethers.constants.AddressZero +const MAX_UINT64 = ethers.BigNumber.from("18446744073709551615") // 2^64 - 1 + +describe("RandomBeacon - Authorization", () => { + let t: T + let randomBeacon: RandomBeacon + let sortitionPool: SortitionPool + let staking: TokenStaking + + let deployer: SignerWithAddress + + let owner: SignerWithAddress + let stakingProvider: SignerWithAddress + let operator: SignerWithAddress + let authorizer: SignerWithAddress + let beneficiary: SignerWithAddress + let thirdParty: SignerWithAddress + let slasher: FakeContract + + const stakedAmount = to1e18(1_000_000) // 1MM T + let minimumAuthorization: BigNumber + + before("load test fixture", async () => { + const contracts = await testDeployment() + + t = contracts.t as T + randomBeacon = contracts.randomBeacon as RandomBeacon + sortitionPool = contracts.sortitionPool as SortitionPool + staking = contracts.staking as TokenStaking + + deployer = await ethers.getNamedSigner("deployer") + ;[owner, stakingProvider, operator, authorizer, beneficiary, thirdParty] = + await ethers.getUnnamedSigners() + + await t.connect(deployer).mint(owner.address, stakedAmount) + await t.connect(owner).approve(staking.address, stakedAmount) + await staking + .connect(owner) + .stake( + stakingProvider.address, + beneficiary.address, + authorizer.address, + stakedAmount + ) + + minimumAuthorization = await randomBeacon.minimumAuthorization() + + // Initialize slasher - fake application capable of slashing the + // staking provider. + slasher = await smock.fake("IApplication") + await staking.connect(deployer).approveApplication(slasher.address) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + slasher.address, + stakedAmount + ) + + // Fund slasher so that it can call T TokenStaking functions + await ( + await ethers.getSigners() + )[0].sendTransaction({ + to: slasher.address, + value: ethers.utils.parseEther("100"), + }) + }) + + describe("registerOperator", () => { + context("when called with zero-address operator", () => { + it("should revert", async () => { + await expect( + randomBeacon.connect(stakingProvider).registerOperator(ZERO_ADDRESS) + ).to.be.revertedWith("Operator can not be zero address") + }) + }) + + // It is not possible to update operator address. Once the operator is + // registered for the given staking provider, it must remain the same. + // Staking provider address is unique for each stake delegation - see T + // TokenStaking contract. + context( + "when operator has been already registered for the staking provider", + () => { + before(async () => { + await createSnapshot() + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + ).to.be.revertedWith("Operator already set for the staking provider") + + // should revert even if it's another operator than the one previously + // registered for the staking provider + await expect( + randomBeacon + .connect(stakingProvider) + .registerOperator(thirdParty.address) + ).to.be.revertedWith("Operator already set for the staking provider") + }) + } + ) + + // Some other staking provider is using the given operator address. + // Should not happen in practice but we should protect against it. + context("when the operator is already in use", () => { + before(async () => { + await createSnapshot() + await randomBeacon + .connect(thirdParty) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + ).to.be.revertedWith("Operator address already in use") + }) + }) + + // This is the normal, happy path. Stake owner delegated their stake to + // the staking provider, and the staking provider is registering operator + // for ECDSA application. + context("when staking provider is registering new operator", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + tx = await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should set staking provider -> operator mapping", async () => { + expect( + await randomBeacon.stakingProviderToOperator(stakingProvider.address) + ).to.equal(operator.address) + }) + + it("should set operator -> staking provider mapping", async () => { + expect( + await randomBeacon.operatorToStakingProvider(operator.address) + ).to.equal(stakingProvider.address) + }) + + it("should emit OperatorRegistered event", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorRegistered") + .withArgs(stakingProvider.address, operator.address) + }) + + it("should not register operator in the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .false + }) + }) + + // It is possible to approve authorization decrease request immediately + // in case the operator was not yet registered by the staking provider. + // It makes sense because non-registered operator could not be in the + // sortition pool, so there is no state that could be not in sync. + // However, we need to ensure this is not exploited by malicious stakers. + // We do not want to let operators with a pending authorization decrease + // request that can be immediately approved to join the sortition pool. + // If there is a pending authorization decrease for the staking provider, + // it must be first approved before operator for that staking provider is + // registered. + context("when there is a pending authorization decrease request", () => { + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + + const deauthorizingBy = to1e18(1) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + ).to.be.revertedWith( + "There is a pending authorization decrease request" + ) + }) + }) + + // This is a continuation of the previous test case - in case there is + // a staking provider who has not yet registered the operator and there is + // an authorization decrease requested for that staking provider, upon + // approving that authorization decrease request, staking provider can + // register an operator. + context("when authorization decrease request was approved", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + + const deauthorizingBy = to1e18(1) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + + tx = await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should set staking provider -> operator mapping", async () => { + expect( + await randomBeacon.stakingProviderToOperator(stakingProvider.address) + ).to.equal(operator.address) + }) + + it("should set operator -> staking provider mapping", async () => { + expect( + await randomBeacon.operatorToStakingProvider(operator.address) + ).to.equal(stakingProvider.address) + }) + + it("should emit OperatorRegistered event", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorRegistered") + .withArgs(stakingProvider.address, operator.address) + }) + }) + }) + + describe("authorizationIncreased", () => { + context("when called not by the staking contract", () => { + it("should revert", async () => { + await expect( + randomBeacon + .connect(thirdParty) + .authorizationIncreased(stakingProvider.address, 0, stakedAmount) + ).to.be.revertedWith("Caller is not the staking contract") + }) + }) + + context("when authorization is below the minimum", () => { + it("should revert", async () => { + await expect( + staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization.sub(1) + ) + ).to.be.revertedWith("Authorization below the minimum") + }) + }) + + // This is normal, happy path for a new delegation. Stake owner delegated + // their stake to the staking provider, and while still being in the + // dashboard (assuming staker is the authorizer), increased authorization + // for ECDSA application. Staking provider has not registered operator yet. + // This will happen later. + context("when the operator is unknown", () => { + // Minimum possible authorization - the minimum authorized amount for + // ECDSA as set in `minimumAuthorization` parameter. + context("when increasing to the minimum possible value", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + tx = await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should emit AuthorizationIncreased", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationIncreased") + .withArgs( + stakingProvider.address, + ZERO_ADDRESS, + 0, + minimumAuthorization + ) + }) + }) + + // Maximum possible authorization - the entire stake delegated to the + // staking provider. + context("when increasing to the maximum possible value", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + tx = await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should emit AuthorizationIncreased", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationIncreased") + .withArgs(stakingProvider.address, ZERO_ADDRESS, 0, stakedAmount) + }) + }) + }) + + // This is normal, happy path for staking provider acting before the + // authorizer, most probably because authorizer is someone else than the + // stake owner. Stake owner delegated their stake to the staking provider, + // staking provider registered operator for ECDSA, and after that, the + // authorizer increased the authorization for the staking provider. + context("when the operator is registered", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + // Minimum possible authorization - the minimum authorized amount for + // ECDSA as set in `minimumAuthorization` parameter. + context("when increasing to the minimum possible value", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + tx = await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should emit AuthorizationIncreased", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationIncreased") + .withArgs( + stakingProvider.address, + operator.address, + 0, + minimumAuthorization + ) + }) + }) + + // Maximum possible authorization - the entire stake delegated to the + // staking provider. + context("when increasing to the maximum possible value", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + tx = await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should emit AuthorizationIncreased", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationIncreased") + .withArgs( + stakingProvider.address, + operator.address, + 0, + stakedAmount + ) + }) + }) + }) + }) + + describe("authorizationDecreaseRequested", () => { + context("when called not by the staking contract", () => { + it("should revert", async () => { + await expect( + randomBeacon + .connect(thirdParty) + .authorizationDecreaseRequested(stakingProvider.address, 100, 99) + ).to.be.revertedWith("Caller is not the staking contract") + }) + }) + + // This is normal happy path in case the stake owner wants to cancel the + // authorization before staking provider started their set up procedure. + // Given the operator was not registered yet by the staking provider, we + // can allow the authorization decrease to be processed immediately if it + // is valid. + context("when the operator is unknown", () => { + before(async () => { + await createSnapshot() + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + // This is not valid authorization decrease request - one most decrease + // to 0 or to some value above the minimum. + context("when decreasing to a non-zero value below the minimum", () => { + it("should revert", async () => { + const deauthorizingTo = minimumAuthorization.sub(1) + const deauthorizingBy = stakedAmount.sub(deauthorizingTo) + + await expect( + staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + ).to.be.revertedWith( + "Authorization amount should be 0 or above the minimum" + ) + }) + }) + + // Decreasing to zero when operator was not set up yet - authorization + // decrease request is valid and can be approved + context("when decreasing to zero", () => { + let tx: ContractTransaction + const decreasingTo = 0 + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require no time delay before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(0) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + const now = await helpers.time.lastBlockTime() + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + ZERO_ADDRESS, + stakedAmount, + decreasingTo, + now + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when decreasing to the minimum", () => { + let tx: ContractTransaction + let decreasingTo + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingTo = minimumAuthorization + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require no time delay before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(0) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + const now = await helpers.time.lastBlockTime() + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + ZERO_ADDRESS, + stakedAmount, + decreasingTo, + now + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when decreasing to a value above the minimum", () => { + let tx: ContractTransaction + let decreasingTo + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingTo = minimumAuthorization.add(1) + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require no time delay before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(0) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + const now = await helpers.time.lastBlockTime() + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + ZERO_ADDRESS, + stakedAmount, + decreasingTo, + now + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when called one more time", () => { + const deauthorizingFirst = to1e18(10) + const deauthorizingSecond = to1e18(20) + + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingFirst + ) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingSecond + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should overwrite the previous request", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.be.equal(deauthorizingSecond) + }) + }) + }) + + // The most popular scenario - operator is registered, it has an + // authorization and that authorization is decreased after some time. + context("when the operator is registered", () => { + before(async () => { + await createSnapshot() + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when decreasing to a non-zero value below the minimum", () => { + it("should revert", async () => { + const deauthorizingTo = minimumAuthorization.sub(1) + const deauthorizingBy = stakedAmount.sub(deauthorizingTo) + + await expect( + staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + ).to.be.revertedWith( + "Authorization amount should be 0 or above the minimum" + ) + }) + }) + + context("when decreasing to zero", () => { + let tx: ContractTransaction + const decreasingTo = 0 + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require updating the pool before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + operator.address, + stakedAmount, + decreasingTo, + MAX_UINT64 + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when decreasing to the minimum", () => { + let tx: ContractTransaction + let decreasingTo + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingTo = minimumAuthorization + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require updating the pool before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + operator.address, + stakedAmount, + decreasingTo, + MAX_UINT64 + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when decreasing to a value above the minimum", () => { + let tx: ContractTransaction + let decreasingTo + let decreasingBy + + before(async () => { + await createSnapshot() + + decreasingTo = minimumAuthorization.add(1) + decreasingBy = stakedAmount.sub(decreasingTo) + tx = await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should require updating the pool before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + + it("should emit AuthorizationDecreaseRequested event", async () => { + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseRequested") + .withArgs( + stakingProvider.address, + operator.address, + stakedAmount, + decreasingTo, + MAX_UINT64 + ) + }) + + it("should capture deauthorizing amount", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(decreasingBy) + }) + }) + + context("when called one more time", () => { + const deauthorizingFirst = to1e18(11) + const deauthorizingSecond = to1e18(21) + + before(async () => { + await createSnapshot() + + await randomBeacon.connect(operator).joinSortitionPool() + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingFirst + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when called before sortition pool was updated", async () => { + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingSecond + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should overwrite the previous request", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.be.equal(deauthorizingSecond) + }) + + it("should require updating the pool before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + }) + + context("when called after sortition pool was updated", async () => { + before(async () => { + await createSnapshot() + + await randomBeacon.updateOperatorStatus(operator.address) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingSecond + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should overwrite the previous request", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.be.equal(deauthorizingSecond) + }) + + it("should require updating the pool one more time before approving", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + }) + }) + }) + }) + + describe("approveAuthorizationDecrease", () => { + before(async () => { + await createSnapshot() + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when decrease was not requested", () => { + it("should revert", async () => { + await expect( + randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + ).to.be.revertedWith("Authorization decrease not requested") + }) + }) + + context("when the operator is unknown", () => { + context("when the decrease was requested", () => { + before(async () => { + await createSnapshot() + + const deauthorizingBy = stakedAmount + + staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should let to approve immediately", async () => { + const tx = await randomBeacon.approveAuthorizationDecrease( + stakingProvider.address + ) + // ok, did not revert + await expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseApproved") + .withArgs(stakingProvider.address) + }) + }) + }) + + context("when the operator is registered", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + const deauthorizingBy = stakedAmount + staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the pool was not updated", () => { + before(async () => { + await createSnapshot() + + // even if we wait for the entire delay, it should not help + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + ).to.be.revertedWith("Authorization decrease request not activated") + }) + }) + + context("when the pool was updated but the delay did not pass", () => { + before(async () => { + await createSnapshot() + + await randomBeacon.updateOperatorStatus(operator.address) + await helpers.time.increaseTime( + params.authorizationDecreaseDelay - 60 // -1min + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + ).to.be.revertedWith("Authorization decrease delay not passed") + }) + }) + + context("when the pool was updated and the delay passed", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + await randomBeacon.updateOperatorStatus(operator.address) + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + + tx = await randomBeacon.approveAuthorizationDecrease( + stakingProvider.address + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should reduce authorized stake amount", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + 0 + ) + }) + + it("should emit AuthorizationDecreaseApproved event", async () => { + expect(tx) + .to.emit(randomBeacon, "AuthorizationDecreaseApproved") + .withArgs(stakingProvider.address) + }) + + it("should clear pending authorization decrease", async () => { + expect( + await randomBeacon.pendingAuthorizationDecrease( + stakingProvider.address + ) + ).to.equal(0) + }) + }) + }) + }) + + describe("involuntaryAuthorizationDecrease", () => { + context("when called not by the staking contract", () => { + it("should revert", async () => { + await expect( + randomBeacon + .connect(thirdParty) + .involuntaryAuthorizationDecrease(stakingProvider.address, 100, 99) + ).to.be.revertedWith("Caller is not the staking contract") + }) + }) + + context("when the operator is unknown", () => { + const slashedAmount = to1e18(100) + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + + // lock the pool for DKG + // we lock the pool to ensure that the update is ignored for the + // operator and that involuntaryAuthorizationDecrease logic in this + // case is basically a pass-through + await randomBeacon.genesis() + + // slash! + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + tx = await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should ignore the update", async () => { + await expect(tx).to.not.emit( + randomBeacon, + "InvoluntaryAuthorizationDecreaseFailed" + ) + }) + }) + + context("when the operator is known", () => { + before(async () => { + await createSnapshot() + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the operator is not in the sortition pool", () => { + const slashedAmount = to1e18(100) + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + // lock the pool for DKG + // we lock the pool to ensure that the update is ignored for the + // operator and that involuntaryAuthorizationDecrease logic in this + // case is basically a pass-through + await randomBeacon.genesis() + + // slash! + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + tx = await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should ignore the update", async () => { + await expect(tx).to.not.emit( + randomBeacon, + "InvoluntaryAuthorizationDecreaseFailed" + ) + }) + }) + + context("when the operator is in the sortition pool", () => { + before(async () => { + await createSnapshot() + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the sortition pool is locked", () => { + const slashedAmount = to1e18(100) + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + // lock the pool for DKG + await randomBeacon.genesis() + + // and slash! + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + tx = await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should not update the pool", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.false + }) + + it("should emit InvoluntaryAuthorizationDecreaseFailed event", async () => { + await expect(tx) + .to.emit(randomBeacon, "InvoluntaryAuthorizationDecreaseFailed") + .withArgs( + stakingProvider.address, + operator.address, + stakedAmount, + stakedAmount.sub(slashedAmount) + ) + }) + }) + + context("when the sortition pool is not locked", () => { + context("when the authorization drops to above the minimum", () => { + const slashedAmount = to1e18(100) + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + // slash! + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + tx = await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should update operator status", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.true + }) + + it("should not emit InvoluntaryAuthorizationDecreaseFailed event", async () => { + await expect(tx).to.not.emit( + randomBeacon, + "InvoluntaryAuthorizationDecreaseFailed" + ) + }) + }) + + context( + "when the authorized amount drops to below the minimum", + () => { + before(async () => { + const slashingTo = minimumAuthorization.sub(1) + const slashingBy = stakedAmount.sub(slashingTo) + + await createSnapshot() + + // slash! + await staking + .connect(slasher.wallet) + .slash(slashingBy, [stakingProvider.address]) + + await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should remove operator from the sortition pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to + .be.false + }) + } + ) + }) + }) + }) + }) + + describe("joinSortitionPool", () => { + context("when the operator is unknown", () => { + it("should revert", async () => { + await expect( + randomBeacon.connect(thirdParty).joinSortitionPool() + ).to.be.revertedWith("Unknown operator") + }) + }) + + context("when the operator has no stake authorized", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon.connect(operator).joinSortitionPool() + ).to.be.revertedWith("Authorization below the minimum") + }) + }) + + // The only option for it to happen is when there was a slashing. + context( + "when the authorization dropped below the minimum but is still non-zero", + () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + const authorizedAmount = minimumAuthorization + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + const slashingTo = minimumAuthorization.sub(1) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. To go below the minimum stake, we need + // to start slashing from the entire staked amount, not just the + // one authorized for RandomBeacon. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should revert", async () => { + await expect( + randomBeacon.connect(operator).joinSortitionPool() + ).to.be.revertedWith("Authorization below the minimum") + }) + } + ) + + context("when the operator has the minimum stake authorized", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization + ) + + tx = await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be.true + }) + + it("should use a correct stake weight", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + minimumAuthorization.div(constants.poolWeightDivisor) + ) + }) + + it("should emit OperatorJoinedSortitionPool", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorJoinedSortitionPool") + .withArgs(stakingProvider.address, operator.address) + }) + }) + + context( + "when the operator has more than the minimum stake authorized", + () => { + let authorizedStake + + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + authorizedStake = minimumAuthorization.mul(2) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedStake + ) + + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .true + }) + + it("should use a correct stake weight", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + authorizedStake.div(constants.poolWeightDivisor) + ) + }) + } + ) + + context("when operator is in the process of deauthorizing", () => { + let deauthorizingTo + + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + const authorizedStake = stakedAmount + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedStake + ) + + deauthorizingTo = minimumAuthorization.add(to1e18(1337)) + const deauthorizingBy = authorizedStake.sub(deauthorizingTo) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be.true + }) + + it("should use a correct stake weight", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + deauthorizingTo.div(constants.poolWeightDivisor) + ) + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + }) + + context( + "when operator is in the process of deauthorizing but also increased authorization in the meantime", + () => { + let expectedAuthorizedStake + + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + const authorizedStake = minimumAuthorization.add(to1e18(100)) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedStake + ) + + const deauthorizingTo = minimumAuthorization.add(to1e18(50)) + const deauthorizingBy = authorizedStake.sub(deauthorizingTo) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + const increasingBy = to1e18(5000) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + increasingBy + ) + + expectedAuthorizedStake = deauthorizingTo.add(increasingBy) + + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .true + }) + + it("should use a correct stake weight", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + expectedAuthorizedStake.div(constants.poolWeightDivisor) + ) + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + } + ) + }) + + describe("updateOperatorStatus", () => { + context("when the operator is unknown", () => { + it("should revert", async () => { + await expect( + randomBeacon.updateOperatorStatus(thirdParty.address) + ).to.be.revertedWith("Unknown operator") + }) + }) + + context("when operator is not in the sortition pool", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the authorization increased", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization + ) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should not insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .false + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + }) + + context("when there was an authorization decrease request", () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakedAmount + ) + + const deauthorizingBy = to1e18(100) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should not insert operator into the pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .false + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + }) + }) + + context("when operator is in the sortition pool", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization.mul(2) + ) + + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the authorization increased", () => { + let tx: ContractTransaction + let expectedWeight + + before(async () => { + await createSnapshot() + + const topUp = to1e18(1337) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + topUp + ) + + // initial authorization was 2 x minimum + // it was increased by 1337 tokens + // so the final authorization should be 2 x minimum + 1337 + expectedWeight = minimumAuthorization + .mul(2) + .add(topUp) + .div(constants.poolWeightDivisor) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should update the pool", async () => { + expect(await sortitionPool.getPoolWeight(operator.address)).to.equal( + expectedWeight + ) + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + }) + + context( + "when there was an authorization decrease request to non-zero", + () => { + let tx: ContractTransaction + let expectedWeight + + before(async () => { + await createSnapshot() + + // initial authorization was 2 x minimum + // we want to decrease to minimum + 1337 + const deauthorizingTo = minimumAuthorization.add(to1e18(1337)) + const deauthorizingBy = minimumAuthorization + .mul(2) + .sub(deauthorizingTo) + expectedWeight = deauthorizingTo.div(constants.poolWeightDivisor) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should update the pool", async () => { + expect( + await sortitionPool.getPoolWeight(operator.address) + ).to.equal(expectedWeight) + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + } + ) + + context( + "when there was an authorization decrease request to zero", + () => { + let tx: ContractTransaction + + before(async () => { + await createSnapshot() + + // initial authorization was 2 x minimum + // we want to decrease to zero + const deauthorizingBy = minimumAuthorization.mul(2) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should remove operator from the sortition pool", async () => { + expect(await randomBeacon.isOperatorInPool(operator.address)).to.be + .false + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + } + ) + + context( + "when operator is in the process of deauthorizing but also increased authorization in the meantime", + () => { + let tx: ContractTransaction + let expectedWeight + + before(async () => { + await createSnapshot() + + // initial authorization was 2 x minimum + // we want to decrease to minimum + 1337 + // and then decrease by 7331 + const deauthorizingTo = minimumAuthorization.add(to1e18(1337)) + const deauthorizingBy = minimumAuthorization + .mul(2) + .sub(deauthorizingTo) + const increasingBy = to1e18(7331) + const increasingTo = deauthorizingTo.add(increasingBy) + expectedWeight = increasingTo.div(constants.poolWeightDivisor) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + increasingBy + ) + + tx = await randomBeacon + .connect(thirdParty) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should update the pool", async () => { + expect( + await sortitionPool.getPoolWeight(operator.address) + ).to.equal(expectedWeight) + }) + + it("should activate authorization decrease delay", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + + it("should emit OperatorStatusUpdated", async () => { + await expect(tx) + .to.emit(randomBeacon, "OperatorStatusUpdated") + .withArgs(stakingProvider.address, operator.address) + }) + } + ) + }) + }) + + describe("eligibleStake", () => { + context("when staking provider has no stake authorized", () => { + it("should return zero", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(0) + }) + }) + + context("when staking provider has stake authorized", () => { + let authorizedAmount + + before(async () => { + await createSnapshot() + + authorizedAmount = minimumAuthorization + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return authorized amount", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(authorizedAmount) + }) + }) + + context( + "when staking provider has some part of the stake deauthorizing", + () => { + let authorizedAmount + let deauthorizingAmount + + before(async () => { + await createSnapshot() + + authorizedAmount = minimumAuthorization.add(to1e18(2000)) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + deauthorizingAmount = to1e18(1337) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return authorized amount minus deauthorizing amount", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(authorizedAmount.sub(deauthorizingAmount)) + }) + } + ) + + context("when staking provider has all of the stake deauthorizing", () => { + before(async () => { + await createSnapshot() + + const authorizedAmount = minimumAuthorization + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return zero", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(0) + }) + }) + + context("when staking provider has all of the stake deauthorized", () => { + before(async () => { + await createSnapshot() + + const authorizedAmount = minimumAuthorization.add(1200) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return zero", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(0) + }) + }) + + // The only option for it to happen is when there was a slashing. + context( + "when the authorization dropped below the minimum but is still non-zero", + () => { + before(async () => { + await createSnapshot() + + const authorizedAmount = minimumAuthorization + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + const slashingTo = minimumAuthorization.sub(1) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. To go below the minimum stake, we need + // to start slashing from the entire staked amount, not just the + // one authorized for RandomBeacon. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return zero", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(0) + }) + } + ) + }) + + describe("remainingAuthorizationDecreaseDelay", () => { + before(async () => { + await createSnapshot() + + const authorizedAmount = minimumAuthorization.add(1200) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + await randomBeacon.connect(operator).joinSortitionPool() + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + authorizedAmount + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + // These tests cover only basic cases. More scenarios such as operator not + // registered for the staking provider has been covered in tests for other + // functions. + + it("should not activate before sortition pool is updated", async () => { + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(MAX_UINT64) + }) + + it("should activate after updating sortition pool", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(params.authorizationDecreaseDelay) + }) + + it("should reduce over time", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + await helpers.time.increaseTime(params.authorizationDecreaseDelay / 2) + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.be.closeTo( + ethers.BigNumber.from(params.authorizationDecreaseDelay / 2), + 5 // +- 5sec + ) + }) + + it("should eventually go to zero", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(0) + + // ...and should remain zero + await helpers.time.increaseTime(3600) // +1h + expect( + await randomBeacon.remainingAuthorizationDecreaseDelay( + stakingProvider.address + ) + ).to.equal(0) + }) + }) + + describe("isOperatorUpToDate", () => { + context("when the operator is unknown", () => { + it("should revert", async () => { + it("should revert", async () => { + await expect( + randomBeacon.isOperatorUpToDate(thirdParty.address) + ).to.be.revertedWith("Unknown operator") + }) + }) + }) + + context("when the operator is not in the sortition pool", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the operator has no authorized stake", () => { + it("should return true", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + context("when the operator has authorized stake", () => { + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should return false", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .false + }) + }) + }) + + context("when the operator is in the sortition pool", () => { + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + minimumAuthorization.mul(2) + ) + + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when the operator just joined the pool", () => { + it("should return true", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + context("when authorization was increased", () => { + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + to1e18(1337) + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when sortition pool was not yet updated", () => { + it("should return false", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.false + }) + }) + + context("when the sortition pool was updated", () => { + it("should return true", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.true + }) + }) + }) + + context("when authorization decrease was requested", () => { + before(async () => { + await createSnapshot() + + const deauthorizingBy = to1e18(1) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + deauthorizingBy + ) + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when sortition pool was not yet updated", () => { + it("should return false", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.false + }) + }) + + context("when the sortition pool was updated", () => { + it("should return true", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.true + }) + }) + }) + + context("when operator was slashed when the pool was locked", () => { + before(async () => { + await createSnapshot() + + // Increase authorization to the maximum possible value and update + // sortition pool. This way, any slashing from `slasher` application + // will affect authorized stake amount for RandomBeacon. + const authorized = await staking.authorizedStake( + stakingProvider.address, + randomBeacon.address + ) + const increaseBy = stakedAmount.sub(authorized) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + increaseBy + ) + await randomBeacon.updateOperatorStatus(operator.address) + + // lock the pool for DKG + await randomBeacon.genesis() + + // and slash! + await staking + .connect(slasher.wallet) + .slash(to1e18(100), [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + + // unlock the pool by stopping DKG + await mineBlocks( + constants.offchainDkgTime + params.dkgResultSubmissionTimeout + ) + await randomBeacon.notifyDkgTimeout() + }) + + after(async () => { + await restoreSnapshot() + }) + + context("when sortition pool was not yet updated", () => { + it("should return false", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.false + }) + }) + + context("when the sortition pool was updated", () => { + it("should return true", async () => { + await randomBeacon.updateOperatorStatus(operator.address) + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to + .be.true + }) + }) + }) + }) + }) + + // Testing final states for scenarios when functions are invoked one after + // another. Operator is known and registered in the sortition pool. + context("mixed interactions", () => { + let initialIncrease + + before(async () => { + await createSnapshot() + + await randomBeacon + .connect(stakingProvider) + .registerOperator(operator.address) + + // Authorized almost the entire staked amount but leave some margin for + // authorization increase. + initialIncrease = stakedAmount.sub(to1e18(20000)) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + initialIncrease + ) + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + // Invoke `increaseAuthorization` after `increaseAuthorization`. + describe("authorizationIncreased -> authorizationIncreased", () => { + let secondIncrease + + before(async () => { + await createSnapshot() + + secondIncrease = to1e18(11111) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + secondIncrease + ) + + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(initialIncrease.add(secondIncrease)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + // Invoke `increaseAuthorization` after `authorizationDecreaseRequested`. + // The decrease is not yet approved when `increaseAuthorization` is called. + describe("authorizationDecreaseRequested -> authorizationIncreased", () => { + let firstDecrease + let secondIncrease + + before(async () => { + await createSnapshot() + + firstDecrease = to1e18(111) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + firstDecrease + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + secondIncrease = to1e18(11111) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + secondIncrease + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(initialIncrease.sub(firstDecrease).add(secondIncrease)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + // Invoke `increaseAuthorization` after `approveAuthorizationDecrease`. + // The decrease is approved when `increaseAuthorization` is called. + describe("non-zero approveAuthorizationDecrease -> authorizationIncreased", () => { + let firstDecrease + let secondIncrease + + before(async () => { + await createSnapshot() + + firstDecrease = to1e18(222) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + firstDecrease + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + + secondIncrease = to1e18(7311) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + secondIncrease + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(initialIncrease.sub(firstDecrease).add(secondIncrease)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + // Invoke `increaseAuthorization` after the authorization was decreased to 0. + describe("to-zero approveAuthorizationDecrease -> authorizationIncreased", () => { + let secondIncrease + + before(async () => { + await createSnapshot() + + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + initialIncrease + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + + secondIncrease = minimumAuthorization.add(to1e18(21)) + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + secondIncrease + ) + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(secondIncrease) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + // Invoke `increaseAuthorization` after `involuntaryAuthorizationDecrease` + // when the authorization amount dropped below the minimum authorization. + describe("below-minimum involuntaryAuthorizationDecrease -> authorizationIncreased", () => { + let slashingTo + let secondIncrease + + before(async () => { + await createSnapshot() + + slashingTo = minimumAuthorization.sub(1) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. To go below the minimum stake, we need + // to start slashing from the entire staked amount, not just the + // one authorized for RandomBeacon. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + + // Given that we slashed from the entire staked amount, we need to give + // the stake owner some more T and let them top-up the stake before they + // increase the authorization again. + secondIncrease = to1e18(10000) + await t.connect(deployer).mint(owner.address, secondIncrease) + await t.connect(owner).approve(staking.address, secondIncrease) + await staking + .connect(owner) + .topUp(stakingProvider.address, secondIncrease) + + // And finally increase! + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + secondIncrease + ) + await randomBeacon.connect(operator).joinSortitionPool() + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(slashingTo.add(secondIncrease)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + describe("authorizationDecreaseRequested -> involuntaryAuthorizationDecrease", () => { + let decreasedAmount + let slashingTo + + before(async () => { + await createSnapshot() + + decreasedAmount = to1e18(20000) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasedAmount + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + slashingTo = initialIncrease.sub(to1e18(100)) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(slashingTo.sub(decreasedAmount)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + describe("authorizationDecreaseRequested -> involuntaryAuthorizationDecrease -> approveAuthorizationDecrease", () => { + let decreasedAmount + let slashingTo + + before(async () => { + await createSnapshot() + + decreasedAmount = to1e18(20000) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasedAmount + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + slashingTo = initialIncrease.sub(to1e18(100)) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(slashingTo.sub(decreasedAmount)) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + + describe("approveAuthorizationDecrease -> involuntaryAuthorizationDecrease", () => { + let decreasedAmount + let slashingTo + + before(async () => { + await createSnapshot() + + decreasedAmount = to1e18(1000) + await staking + .connect(authorizer) + ["requestAuthorizationDecrease(address,address,uint96)"]( + stakingProvider.address, + randomBeacon.address, + decreasedAmount + ) + await randomBeacon + .connect(operator) + .updateOperatorStatus(operator.address) + + await helpers.time.increaseTime(params.authorizationDecreaseDelay) + await randomBeacon.approveAuthorizationDecrease(stakingProvider.address) + + slashingTo = initialIncrease.sub(to1e18(2500)) + // Note that we slash from the entire staked amount given that the + // initially authorized amount is less than staked amount and it is + // another application slashing. + const slashedAmount = stakedAmount.sub(slashingTo) + + await staking + .connect(slasher.wallet) + .slash(slashedAmount, [stakingProvider.address]) + await staking.connect(thirdParty).processSlashing(1) + }) + + after(async () => { + await restoreSnapshot() + }) + + it("should have correct eligible stake", async () => { + expect( + await randomBeacon.eligibleStake(stakingProvider.address) + ).to.equal(slashingTo) + }) + + it("should have operator status updated", async () => { + expect(await randomBeacon.isOperatorUpToDate(operator.address)).to.be + .true + }) + }) + }) +}) diff --git a/solidity/random-beacon/test/RandomBeacon.Callback.test.ts b/solidity/random-beacon/test/RandomBeacon.Callback.test.ts index d18c54e491..0569dc913a 100644 --- a/solidity/random-beacon/test/RandomBeacon.Callback.test.ts +++ b/solidity/random-beacon/test/RandomBeacon.Callback.test.ts @@ -1,4 +1,6 @@ -import { ethers, waffle, helpers, getUnnamedAccounts } from "hardhat" +/* eslint-disable @typescript-eslint/no-extra-semi */ + +import { ethers, waffle, helpers } from "hardhat" import { expect } from "chai" import blsData from "./data/bls" @@ -9,7 +11,7 @@ import { registerOperators } from "./utils/operators" import type { DeployedContracts } from "./fixtures" import type { RandomBeaconStub, - TestToken, + T, CallbackContractStub, RandomBeacon, } from "../typechain" @@ -23,7 +25,7 @@ const fixture = async () => { const contracts: DeployedContracts = { randomBeacon: deployment.randomBeacon, - testToken: deployment.testToken, + t: deployment.t, callbackContractStub: await ( await ethers.getContractFactory("CallbackContractStub") ).deploy(), @@ -32,11 +34,13 @@ const fixture = async () => { ).deploy(), } - // Accounts offset provided to slice getUnnamedAccounts have to include number + // Accounts offset provided to slice getUnnamedSigners have to include number // of unnamed accounts that were already used. const signers = await registerOperators( contracts.randomBeacon as RandomBeacon, - (await getUnnamedAccounts()).slice(1, 1 + constants.groupSize) + contracts.t as T, + constants.groupSize, + 2 ) await createGroup(contracts.randomBeacon as RandomBeacon, signers) @@ -49,18 +53,17 @@ describe("RandomBeacon - Callback", () => { let submitter: SignerWithAddress let randomBeacon: RandomBeaconStub - let testToken: TestToken + let t: T let callbackContract: CallbackContractStub let callbackContract1: CallbackContractStub before(async () => { - requester = await ethers.getSigner((await getUnnamedAccounts())[1]) - submitter = await ethers.getSigner((await getUnnamedAccounts())[2]) + ;[requester, submitter] = await ethers.getUnnamedSigners() const { contracts } = await waffle.loadFixture(fixture) randomBeacon = contracts.randomBeacon as RandomBeaconStub - testToken = contracts.testToken as TestToken + t = contracts.t as T callbackContract = contracts.callbackContractStub as CallbackContractStub callbackContract1 = contracts.callbackContractStub1 as CallbackContractStub }) @@ -69,7 +72,7 @@ describe("RandomBeacon - Callback", () => { before(async () => { await createSnapshot() - await approveTestToken() + await approveTokenForFee() }) after(async () => { @@ -102,7 +105,7 @@ describe("RandomBeacon - Callback", () => { .connect(submitter) ["submitRelayEntry(bytes)"](blsData.groupSignature) - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) @@ -124,7 +127,7 @@ describe("RandomBeacon - Callback", () => { .connect(submitter) ["submitRelayEntry(bytes)"](blsData.groupSignature) - await approveTestToken() + await approveTokenForFee() await randomBeacon .connect(requester) @@ -143,7 +146,7 @@ describe("RandomBeacon - Callback", () => { before(async () => { await createSnapshot() - await approveTestToken() + await approveTokenForFee() }) after(async () => { @@ -223,9 +226,9 @@ describe("RandomBeacon - Callback", () => { }) }) - async function approveTestToken() { - await testToken.mint(requester.address, params.relayRequestFee) - await testToken + async function approveTokenForFee() { + await t.mint(requester.address, params.relayRequestFee) + await t .connect(requester) .approve(randomBeacon.address, params.relayRequestFee) } diff --git a/solidity/random-beacon/test/RandomBeacon.GroupCreation.test.ts b/solidity/random-beacon/test/RandomBeacon.GroupCreation.test.ts index 750932cc18..4385ffd846 100644 --- a/solidity/random-beacon/test/RandomBeacon.GroupCreation.test.ts +++ b/solidity/random-beacon/test/RandomBeacon.GroupCreation.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-expressions, @typescript-eslint/no-extra-semi */ -import { ethers, waffle, helpers, getUnnamedAccounts } from "hardhat" +import { ethers, waffle, helpers } from "hardhat" import { expect } from "chai" import blsData from "./data/bls" @@ -18,19 +18,18 @@ import { registerOperators } from "./utils/operators" import { selectGroup, createGroup, hashUint32Array } from "./utils/groups" import { fakeTokenStaking } from "./mocks/staking" +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" +import type { BigNumber, ContractTransaction, Signer } from "ethers" import type { Operator } from "./utils/operators" import type { BeaconDkg as DKG } from "../typechain/RandomBeaconStub" import type { FakeContract } from "@defi-wonderland/smock" -import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import type { RandomBeacon, RandomBeaconGovernance, - TestToken, + T, SortitionPool, - StakingStub, - IRandomBeaconStaking, + TokenStaking, } from "../typechain" -import type { BigNumber, ContractTransaction, Signer } from "ethers" const { mineBlocks, mineBlocksTo } = helpers.time const { to1e18 } = helpers.number @@ -46,26 +45,28 @@ type RandomBeaconTest = RandomBeacon & { const fixture = async () => { const contracts = await testDeployment() - // Accounts offset provided to slice getUnnamedAccounts have to include number + // Accounts offset provided to slice getUnnamedSigners have to include number // of unnamed accounts that were already used. const signers = await registerOperators( contracts.randomBeacon as RandomBeacon, - (await getUnnamedAccounts()).slice(1, 1 + constants.groupSize) + contracts.t as T, + constants.groupSize, + 1 ) const randomBeaconGovernance = contracts.randomBeaconGovernance as RandomBeaconGovernance const randomBeacon = contracts.randomBeacon as RandomBeaconTest const sortitionPool = contracts.sortitionPool as SortitionPool - const staking = contracts.stakingStub as StakingStub - const testToken = contracts.testToken as TestToken + const staking = contracts.staking as TokenStaking + const t = contracts.t as T return { randomBeaconGovernance, randomBeacon, sortitionPool, staking, - testToken, + t, signers, } } @@ -77,24 +78,24 @@ describe("RandomBeacon - Group Creation", () => { constants.offchainDkgTime + params.dkgResultSubmissionTimeout const groupPublicKey: string = ethers.utils.hexValue(blsData.groupPubKey) - let thirdParty: Signer + let thirdParty: SignerWithAddress let signers: Operator[] let randomBeaconGovernance: RandomBeaconGovernance let randomBeacon: RandomBeaconTest let sortitionPool: SortitionPool - let staking: StakingStub - let testToken: TestToken + let staking: TokenStaking + let t: T before(async () => { - thirdParty = await ethers.getSigner((await getUnnamedAccounts())[0]) + ;[thirdParty] = await ethers.getUnnamedSigners() let randomBeaconStub: RandomBeaconTest ;({ randomBeaconGovernance, randomBeacon: randomBeaconStub, sortitionPool, staking, - testToken, + t, signers, } = await waffle.loadFixture(fixture)) @@ -1453,7 +1454,7 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - initialSubmitterBalance = await testToken.balanceOf( + initialSubmitterBalance = await t.balanceOf( await submitter.getAddress() ) tx = await randomBeacon @@ -1509,8 +1510,9 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance.sub(currentDkgRewardsPoolBalance) ).to.be.equal(params.dkgResultSubmissionReward) - const currentSubmitterBalance: BigNumber = - await testToken.balanceOf(await submitter.getAddress()) + const currentSubmitterBalance: BigNumber = await t.balanceOf( + await submitter.getAddress() + ) expect( currentSubmitterBalance.sub(initialSubmitterBalance) ).to.be.equal(params.dkgResultSubmissionReward) @@ -1555,7 +1557,7 @@ describe("RandomBeacon - Group Creation", () => { await mineBlocks(params.dkgSubmitterPrecedencePeriodLength) initialDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - initApproverBalance = await testToken.balanceOf( + initApproverBalance = await t.balanceOf( await thirdParty.getAddress() ) tx = randomBeacon @@ -1580,7 +1582,7 @@ describe("RandomBeacon - Group Creation", () => { ) ).to.be.equal(params.dkgResultSubmissionReward) - const currentApproverBalance = await testToken.balanceOf( + const currentApproverBalance = await t.balanceOf( await thirdParty.getAddress() ) expect( @@ -1680,7 +1682,7 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - initialSubmitterBalance = await testToken.balanceOf( + initialSubmitterBalance = await t.balanceOf( await anotherSubmitter.getAddress() ) @@ -1731,8 +1733,9 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance.sub(currentDkgRewardsPoolBalance) ).to.be.equal(params.dkgResultSubmissionReward) - const currentSubmitterBalance: BigNumber = - await testToken.balanceOf(await anotherSubmitter.getAddress()) + const currentSubmitterBalance: BigNumber = await t.balanceOf( + await anotherSubmitter.getAddress() + ) expect( currentSubmitterBalance.sub(initialSubmitterBalance) ).to.be.equal(params.dkgResultSubmissionReward) @@ -1947,9 +1950,7 @@ describe("RandomBeacon - Group Creation", () => { noMisbehaved )) - initApproverBalance = await testToken.balanceOf( - await submitter.getAddress() - ) + initApproverBalance = await t.balanceOf(await submitter.getAddress()) await mineBlocks(params.dkgResultChallengePeriodLength) tx = randomBeacon.connect(submitter).approveDkgResult(dkgResult) @@ -1966,7 +1967,7 @@ describe("RandomBeacon - Group Creation", () => { it("should pay the approver the whole DKG rewards pool balance", async () => { expect(await randomBeacon.dkgRewardsPool()).to.be.equal(0) - const currentApproverBalance = await testToken.balanceOf( + const currentApproverBalance = await t.balanceOf( await submitter.getAddress() ) expect(currentApproverBalance.sub(initApproverBalance)).to.be.equal( @@ -2078,7 +2079,7 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - initialNotifierBalance = await testToken.balanceOf( + initialNotifierBalance = await t.balanceOf( await thirdParty.getAddress() ) tx = await randomBeacon.connect(thirdParty).notifyDkgTimeout() @@ -2103,7 +2104,7 @@ describe("RandomBeacon - Group Creation", () => { initialDkgRewardsPoolBalance.sub(currentDkgRewardsPoolBalance) ).to.be.equal(params.sortitionPoolUnlockingReward) - const currentNotifierBalance: BigNumber = await testToken.balanceOf( + const currentNotifierBalance: BigNumber = await t.balanceOf( await thirdParty.getAddress() ) expect( @@ -2299,13 +2300,17 @@ describe("RandomBeacon - Group Creation", () => { context("at the beginning of challenge period", async () => { context("called by a third party", async () => { - let tx: ContractTransaction + let challengeTx: ContractTransaction + let slashingTx: ContractTransaction + before(async () => { await createSnapshot() - tx = await randomBeacon + challengeTx = await randomBeacon .connect(thirdParty) .challengeDkgResult(dkgResult) + + slashingTx = await staking.processSlashing(1) }) after(async () => { @@ -2313,7 +2318,7 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgResultChallenged event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgResultChallenged") .withArgs( dkgResultHash, @@ -2327,17 +2332,48 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgMaliciousResultSlashed event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgMaliciousResultSlashed") - .withArgs(dkgResultHash, to1e18(50000), submitter.address) + .withArgs( + dkgResultHash, + params.maliciousDkgResultSlashingAmount, + submitter.address + ) + }) + + it("should not emit DkgMaliciousResultSlashingFailed event", async () => { + await expect(challengeTx).to.not.emit( + randomBeacon, + "DkgMaliciousResultSlashingFailed" + ) + }) + + it("should reward the notifier", async () => { + await expect(challengeTx) + .to.emit(staking, "NotifierRewarded") + .withArgs( + thirdParty.address, + constants.tokenStakingNotificationReward + .mul( + params.dkgMaliciousResultNotificationRewardMultiplier + ) + .div(100) + ) }) it("should slash malicious result submitter", async () => { - await expect(tx) - .to.emit(staking, "Seized") - .withArgs(to1e18(50000), 100, await thirdParty.getAddress(), [ - submitter.address, - ]) + const stakingProvider = + await randomBeacon.operatorToStakingProvider( + submitter.address + ) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.maliciousDkgResultSlashingAmount, + false + ) }) }) }) @@ -2358,13 +2394,17 @@ describe("RandomBeacon - Group Creation", () => { }) context("called by a third party", async () => { - let tx: ContractTransaction + let challengeTx: ContractTransaction + let slashingTx: ContractTransaction + before(async () => { await createSnapshot() - tx = await randomBeacon + challengeTx = await randomBeacon .connect(thirdParty) .challengeDkgResult(dkgResult) + + slashingTx = await staking.processSlashing(1) }) after(async () => { @@ -2372,7 +2412,7 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgResultChallenged event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgResultChallenged") .withArgs( dkgResultHash, @@ -2386,17 +2426,48 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgMaliciousResultSlashed event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgMaliciousResultSlashed") - .withArgs(dkgResultHash, to1e18(50000), submitter.address) + .withArgs( + dkgResultHash, + params.maliciousDkgResultSlashingAmount, + submitter.address + ) + }) + + it("should not emit DkgMaliciousResultSlashingFailed event", async () => { + await expect(challengeTx).to.not.emit( + randomBeacon, + "DkgMaliciousResultSlashingFailed" + ) + }) + + it("should reward the notifier", async () => { + await expect(challengeTx) + .to.emit(staking, "NotifierRewarded") + .withArgs( + thirdParty.address, + constants.tokenStakingNotificationReward + .mul( + params.dkgMaliciousResultNotificationRewardMultiplier + ) + .div(100) + ) }) it("should slash malicious result submitter", async () => { - await expect(tx) - .to.emit(staking, "Seized") - .withArgs(to1e18(50000), 100, await thirdParty.getAddress(), [ - submitter.address, - ]) + const stakingProvider = + await randomBeacon.operatorToStakingProvider( + submitter.address + ) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.maliciousDkgResultSlashingAmount, + false + ) }) }) }) @@ -2421,15 +2492,16 @@ describe("RandomBeacon - Group Creation", () => { }) }) - context("with token staking seize call failure", async () => { - let tokenStakingFake: FakeContract + // FIXME: Blocked by https://github.com/defi-wonderland/smock/issues/101 + context.skip("with token staking seize call failure", async () => { + let tokenStakingFake: FakeContract let tx: Promise before(async () => { await createSnapshot() tokenStakingFake = await fakeTokenStaking(randomBeacon) - tokenStakingFake.seize.reverts() + tokenStakingFake.seize.reverts("faked function revert") tx = randomBeacon .connect(thirdParty) @@ -2483,7 +2555,8 @@ describe("RandomBeacon - Group Creation", () => { let dkgResultHash: string let dkgResult: DKG.ResultStruct let submitter: SignerWithAddress - let tx: ContractTransaction + let challengeTx: ContractTransaction + let slashingTx: ContractTransaction before(async () => { await createSnapshot() @@ -2496,9 +2569,11 @@ describe("RandomBeacon - Group Creation", () => { noMisbehaved )) - tx = await randomBeacon + challengeTx = await randomBeacon .connect(thirdParty) .challengeDkgResult(dkgResult) + + slashingTx = await staking.processSlashing(1) }) after(async () => { @@ -2506,7 +2581,7 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgResultChallenged event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgResultChallenged") .withArgs( dkgResultHash, @@ -2520,17 +2595,44 @@ describe("RandomBeacon - Group Creation", () => { }) it("should emit DkgMaliciousResultSlashed event", async () => { - await expect(tx) + await expect(challengeTx) .to.emit(randomBeacon, "DkgMaliciousResultSlashed") - .withArgs(dkgResultHash, to1e18(50000), submitter.address) + .withArgs( + dkgResultHash, + params.maliciousDkgResultSlashingAmount, + submitter.address + ) + }) + + it("should not emit DkgMaliciousResultSlashingFailed event", async () => { + await expect(challengeTx).to.not.emit( + randomBeacon, + "DkgMaliciousResultSlashingFailed" + ) + }) + + it("should reward the notifier", async () => { + await expect(challengeTx) + .to.emit(staking, "NotifierRewarded") + .withArgs( + thirdParty.address, + constants.tokenStakingNotificationReward + .mul(params.dkgMaliciousResultNotificationRewardMultiplier) + .div(100) + ) }) it("should slash malicious result submitter", async () => { - await expect(tx) - .to.emit(staking, "Seized") - .withArgs(to1e18(50000), 100, await thirdParty.getAddress(), [ - submitter.address, - ]) + const stakingProvider = + await randomBeacon.operatorToStakingProvider(submitter.address) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.maliciousDkgResultSlashingAmount, + false + ) }) } ) @@ -2705,12 +2807,10 @@ describe("RandomBeacon - Group Creation", () => { await createSnapshot() previousDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - previousRandomBeaconBalance = await testToken.balanceOf( - randomBeacon.address - ) + previousRandomBeaconBalance = await t.balanceOf(randomBeacon.address) - await testToken.mint(await thirdParty.getAddress(), amount) - await testToken.connect(thirdParty).approve(randomBeacon.address, amount) + await t.mint(await thirdParty.getAddress(), amount) + await t.connect(thirdParty).approve(randomBeacon.address, amount) await randomBeacon.fundDkgRewardsPool( await thirdParty.getAddress(), @@ -2730,9 +2830,7 @@ describe("RandomBeacon - Group Creation", () => { }) it("should transfer tokens to the random beacon contract", async () => { - const currentRandomBeaconBalance = await testToken.balanceOf( - randomBeacon.address - ) + const currentRandomBeaconBalance = await t.balanceOf(randomBeacon.address) expect( currentRandomBeaconBalance.sub(previousRandomBeaconBalance) ).to.be.equal(amount) @@ -2740,10 +2838,8 @@ describe("RandomBeacon - Group Creation", () => { }) async function fundDkgRewardsPool(donateAmount: BigNumber) { - await testToken.mint(await thirdParty.getAddress(), donateAmount) - await testToken - .connect(thirdParty) - .approve(randomBeacon.address, donateAmount) + await t.mint(await thirdParty.getAddress(), donateAmount) + await t.connect(thirdParty).approve(randomBeacon.address, donateAmount) await randomBeacon.fundDkgRewardsPool( await thirdParty.getAddress(), diff --git a/solidity/random-beacon/test/RandomBeacon.Pool.test.ts b/solidity/random-beacon/test/RandomBeacon.Pool.test.ts deleted file mode 100644 index 9733007ad2..0000000000 --- a/solidity/random-beacon/test/RandomBeacon.Pool.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions */ - -import { ethers, waffle } from "hardhat" -import { expect } from "chai" - -import { randomBeaconDeployment, constants } from "./fixtures" - -import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" -import type { RandomBeaconStub, SortitionPool, StakingStub } from "../typechain" - -const fixture = async () => randomBeaconDeployment() - -describe("RandomBeacon - Pool", () => { - let operator: SignerWithAddress - let randomBeacon: RandomBeaconStub - let sortitionPool: SortitionPool - let stakingStub: StakingStub - - // prettier-ignore - before(async () => { - [operator] = await ethers.getSigners() - }) - - beforeEach("load test fixture", async () => { - const contracts = await waffle.loadFixture(fixture) - - randomBeacon = contracts.randomBeacon as RandomBeaconStub - sortitionPool = contracts.sortitionPool as SortitionPool - stakingStub = contracts.stakingStub as StakingStub - }) - - describe("registerOperator", () => { - beforeEach(async () => { - await stakingStub.setStake(operator.address, constants.minimumStake) - }) - - context("when the operator is not registered yet", () => { - beforeEach(async () => { - await randomBeacon.connect(operator).registerOperator() - }) - - it("should register the operator", async () => { - expect(await sortitionPool.isOperatorInPool(operator.address)).to.be - .true - }) - }) - - context("when the operator is already registered", () => { - beforeEach(async () => { - await randomBeacon.connect(operator).registerOperator() - }) - - it("should revert", async () => { - await expect( - randomBeacon.connect(operator).registerOperator() - ).to.be.revertedWith("Operator is already registered") - }) - }) - }) - - describe("updateOperatorStatus", () => { - beforeEach(async () => { - // Operator is registered. - await stakingStub.setStake(operator.address, constants.minimumStake) - await randomBeacon.connect(operator).registerOperator() - - // Simulate the operator became ineligible. - await stakingStub.setStake(operator.address, 0) - - await randomBeacon.connect(operator).updateOperatorStatus() - }) - - context("when status update removes operator from sortition pool", () => { - it("should remove operator from the pool", async () => { - expect(await sortitionPool.isOperatorInPool(operator.address)).to.be - .false - }) - }) - }) -}) diff --git a/solidity/random-beacon/test/RandomBeacon.Relay.test.ts b/solidity/random-beacon/test/RandomBeacon.Relay.test.ts index d73f5c486c..acc91d674f 100644 --- a/solidity/random-beacon/test/RandomBeacon.Relay.test.ts +++ b/solidity/random-beacon/test/RandomBeacon.Relay.test.ts @@ -1,12 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-expressions, no-await-in-loop, @typescript-eslint/no-extra-semi */ -import { - ethers, - waffle, - helpers, - getUnnamedAccounts, - getNamedAccounts, -} from "hardhat" +import { ethers, waffle, helpers } from "hardhat" import { expect } from "chai" import { BigNumber } from "ethers" @@ -28,16 +22,15 @@ import type { FakeContract } from "@defi-wonderland/smock" import type { RandomBeacon, RandomBeaconStub, - TestToken, + T, RelayStub, SortitionPool, - StakingStub, + TokenStaking, BLS, RandomBeaconGovernance, - IRandomBeaconStaking, } from "../typechain" import type { Address } from "hardhat-deploy/types" -import type { ContractTransaction, BigNumberish, BytesLike } from "ethers" +import type { ContractTransaction, BigNumberish } from "ethers" import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" const { mineBlocks, mineBlocksTo } = helpers.time @@ -62,9 +55,13 @@ async function fixture() { // Register operators in the sortition pool to make group creation // possible. + // Accounts offset provided to slice getUnnamedSigners have to include number + // of unnamed accounts that were already used. const operators = await registerOperators( deployment.randomBeacon as RandomBeacon, - (await getUnnamedAccounts()).slice(0, constants.groupSize) + deployment.t as T, + constants.groupSize, + 4 ) return { @@ -72,8 +69,8 @@ async function fixture() { randomBeaconGovernance: deployment.randomBeaconGovernance as RandomBeaconGovernance, sortitionPool: deployment.sortitionPool as SortitionPool, - testToken: deployment.testToken as TestToken, - staking: deployment.stakingStub as StakingStub, + t: deployment.t as T, + staking: deployment.staking as TokenStaking, relayStub, bls, operators, @@ -92,21 +89,19 @@ describe("RandomBeacon - Relay", () => { let randomBeacon: RandomBeaconTest let sortitionPool: SortitionPool - let testToken: TestToken - let staking: StakingStub + let t: T + let staking: TokenStaking let relayStub: RelayStub let bls: BLS before(async () => { - deployer = await ethers.getSigner((await getNamedAccounts()).deployer) - thirdParty = await ethers.getSigner((await getNamedAccounts()).thirdParty) - requester = await ethers.getSigner((await getUnnamedAccounts())[1]) - notifier = await ethers.getSigner((await getUnnamedAccounts())[2]) - submitter = await ethers.getSigner((await getUnnamedAccounts())[3]) + deployer = await ethers.getNamedSigner("deployer") + ;[thirdParty, requester, notifier, submitter] = + await ethers.getUnnamedSigners() ;({ randomBeacon, sortitionPool, - testToken, + t, staking, relayStub, bls, @@ -114,7 +109,7 @@ describe("RandomBeacon - Relay", () => { } = await waffle.loadFixture(fixture)) membersIDs = members.map((member) => member.id) - membersAddresses = members.map((member) => member.address) + membersAddresses = members.map((member) => member.signer.address) }) describe("requestRelayEntry", () => { @@ -139,10 +134,10 @@ describe("RandomBeacon - Relay", () => { await createSnapshot() previousDkgRewardsPoolBalance = await randomBeacon.dkgRewardsPool() - previousRandomBeaconBalance = await testToken.balanceOf( + previousRandomBeaconBalance = await t.balanceOf( randomBeacon.address ) - await approveTestToken() + await approveTokenForFee() }) after(async () => { @@ -175,7 +170,7 @@ describe("RandomBeacon - Relay", () => { ).to.be.equal(params.relayRequestFee) // Assert actual transfer took place. - const currentRandomBeaconBalance = await testToken.balanceOf( + const currentRandomBeaconBalance = await t.balanceOf( randomBeacon.address ) expect( @@ -229,7 +224,7 @@ describe("RandomBeacon - Relay", () => { ).to.be.equal(params.relayRequestFee) // Assert actual transfer took place. - const currentRandomBeaconBalance = await testToken.balanceOf( + const currentRandomBeaconBalance = await t.balanceOf( randomBeacon.address ) expect( @@ -270,7 +265,7 @@ describe("RandomBeacon - Relay", () => { before(async () => { await createSnapshot() - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) }) @@ -310,7 +305,7 @@ describe("RandomBeacon - Relay", () => { before(async () => { await createSnapshot() - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) }) @@ -334,8 +329,13 @@ describe("RandomBeacon - Relay", () => { await restoreSnapshot() }) + it("should not reward the notifier", async () => { + await expect(tx).to.not.emit(staking, "NotifierRewarded") + }) + it("should not slash any members", async () => { - await expect(tx).to.not.emit(staking, "Slashed") + await expect(tx).to.not.emit(staking, "TokensSeized") + expect(await staking.getSlashingQueueLength()).to.be.equal(0) }) it("should emit RelayEntrySubmitted event", async () => { @@ -422,7 +422,7 @@ describe("RandomBeacon - Relay", () => { before(async () => { await createSnapshot() - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) }) @@ -448,8 +448,13 @@ describe("RandomBeacon - Relay", () => { await restoreSnapshot() }) - it("should not slash members ", async () => { - await expect(tx).to.not.emit(staking, "Slashed") + it("should not reward the notifier", async () => { + await expect(tx).to.not.emit(staking, "NotifierRewarded") + }) + + it("should not slash any members", async () => { + await expect(tx).to.not.emit(staking, "TokensSeized") + expect(await staking.getSlashingQueueLength()).to.be.equal(0) await expect(tx).to.not.emit(randomBeacon, "RelayEntryDelaySlashed") }) @@ -466,7 +471,13 @@ describe("RandomBeacon - Relay", () => { }) context("when result is submitted after the soft timeout", () => { - let tx: ContractTransaction + // `relayEntrySubmissionFailureSlashingAmount = 1000e18`. + // 75% of the soft timeout period elapsed so we expect + // `750e18` to be slashed. + const slashingAmount = to1e18(750) + + let submissionTx: ContractTransaction + let slashingTx: ContractTransaction before(async () => { await createSnapshot() @@ -484,33 +495,46 @@ describe("RandomBeacon - Relay", () => { 0.75 * params.relayEntryHardTimeout - 1 ) - tx = await randomBeacon + submissionTx = await randomBeacon .connect(submitter) ["submitRelayEntry(bytes,uint32[])"]( blsData.groupSignature, membersIDs ) + + slashingTx = await staking.processSlashing(membersAddresses.length) }) after(async () => { await restoreSnapshot() }) - it("should slash a correct portion of the slashing amount for all members ", async () => { - // `relayEntrySubmissionFailureSlashingAmount = 1000e18`. - // 75% of the soft timeout period elapsed so we expect - // `750e18` to be slashed. - await expect(tx) - .to.emit(staking, "Slashed") - .withArgs(to1e18(750), membersAddresses) + // TokenStaking.slash function is called that doesn't reward the notifier. + it("should not reward the notifier", async () => { + await expect(submissionTx).to.not.emit(staking, "NotifierRewarded") + }) - await expect(tx) + it("should slash a correct portion of the slashing amount for all group members", async () => { + for (let i = 0; i < membersAddresses.length; i++) { + const stakingProvider = + await randomBeacon.operatorToStakingProvider( + membersAddresses[i] + ) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs(stakingProvider, slashingAmount, false) + } + }) + + it("should emit RelayEntryDelaySlashed event", async () => { + await expect(submissionTx) .to.emit(randomBeacon, "RelayEntryDelaySlashed") - .withArgs(1, to1e18(750), membersAddresses) + .withArgs(1, slashingAmount, membersAddresses) }) it("should emit RelayEntrySubmitted event", async () => { - await expect(tx) + await expect(submissionTx) .to.emit(randomBeacon, "RelayEntrySubmitted") .withArgs(1, submitter.address, blsData.groupSignature) }) @@ -583,7 +607,7 @@ describe("RandomBeacon - Relay", () => { await createSnapshot() await createGroup(randomBeacon, members) - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) }) @@ -607,7 +631,8 @@ describe("RandomBeacon - Relay", () => { context( "when other active groups exist after timeout is reported", () => { - let tx: ContractTransaction + let reportTx: ContractTransaction + let slashingTx: ContractTransaction before(async () => { await createSnapshot() @@ -617,28 +642,61 @@ describe("RandomBeacon - Relay", () => { hashUint32Array(membersIDs) ) - tx = await randomBeacon + reportTx = await randomBeacon .connect(notifier) .reportRelayEntryTimeout(membersIDs) + + slashingTx = await staking.processSlashing(membersAddresses.length) }) after(async () => { await restoreSnapshot() }) - it("should slash the full slashing amount for all group members", async () => { - await expect(tx) - .to.emit(staking, "Seized") + it("should reward the notifier", async () => { + await expect(reportTx) + .to.emit(staking, "NotifierRewarded") .withArgs( - to1e18(1000), - params.relayEntryTimeoutNotificationRewardMultiplier, notifier.address, - membersAddresses + constants.tokenStakingNotificationReward + .mul(params.relayEntryTimeoutNotificationRewardMultiplier) + .div(100) + .mul(membersIDs.length) ) + }) - await expect(tx) + it("should slash the full slashing amount for all group members", async () => { + for (let i = 0; i < membersAddresses.length; i++) { + const stakingProvider = + await randomBeacon.operatorToStakingProvider( + membersAddresses[i] + ) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.relayEntrySubmissionFailureSlashingAmount, + false + ) + } + }) + + it("should emit RelayEntryTimeoutSlashed event", async () => { + await expect(reportTx) .to.emit(randomBeacon, "RelayEntryTimeoutSlashed") - .withArgs(1, to1e18(1000), membersAddresses) + .withArgs( + 1, + params.relayEntrySubmissionFailureSlashingAmount, + membersAddresses + ) + }) + + it("should not emit RelayEntryTimeoutSlashingFailed event", async () => { + await expect(reportTx).to.not.emit( + randomBeacon, + "RelayEntryTimeoutSlashingFailed" + ) }) it("should terminate the group", async () => { @@ -646,7 +704,7 @@ describe("RandomBeacon - Relay", () => { }) it("should emit RelayEntryTimedOut event", async () => { - await expect(tx) + await expect(reportTx) .to.emit(randomBeacon, "RelayEntryTimedOut") .withArgs(1, 0) }) @@ -655,7 +713,7 @@ describe("RandomBeacon - Relay", () => { // We expect the same request ID because this is a retry. // Group ID is `1` because we take an active group from `groupsRegistry` // array. Group with an index `0` was terminated. - await expect(tx) + await expect(reportTx) .to.emit(randomBeacon, "RelayEntryRequested") .withArgs(1, 1, blsData.previousEntry) @@ -710,33 +768,65 @@ describe("RandomBeacon - Relay", () => { ) context("when no active groups exist after timeout is reported", () => { - let tx: ContractTransaction + let reportTx: ContractTransaction + let slashingTx: ContractTransaction before(async () => { await createSnapshot() - tx = await randomBeacon + reportTx = await randomBeacon .connect(notifier) .reportRelayEntryTimeout(membersIDs) + + slashingTx = await staking.processSlashing(membersAddresses.length) }) after(async () => { await restoreSnapshot() }) - it("should slash the full slashing amount for all group members", async () => { - await expect(tx) - .to.emit(staking, "Seized") + it("should reward the notifier", async () => { + await expect(reportTx) + .to.emit(staking, "NotifierRewarded") .withArgs( - to1e18(1000), - params.relayEntryTimeoutNotificationRewardMultiplier, notifier.address, - membersAddresses + constants.tokenStakingNotificationReward + .mul(params.relayEntryTimeoutNotificationRewardMultiplier) + .div(100) + .mul(membersIDs.length) ) + }) - await expect(tx) + it("should slash the full slashing amount for all group members", async () => { + for (let i = 0; i < membersAddresses.length; i++) { + const stakingProvider = + await randomBeacon.operatorToStakingProvider(membersAddresses[i]) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.relayEntrySubmissionFailureSlashingAmount, + false + ) + } + }) + + it("should emit RelayEntryTimeoutSlashed event", async () => { + await expect(reportTx) .to.emit(randomBeacon, "RelayEntryTimeoutSlashed") - .withArgs(1, to1e18(1000), membersAddresses) + .withArgs( + 1, + params.relayEntrySubmissionFailureSlashingAmount, + membersAddresses + ) + }) + + it("should not emit RelayEntryTimeoutSlashingFailed event", async () => { + await expect(reportTx).to.not.emit( + randomBeacon, + "RelayEntryTimeoutSlashingFailed" + ) }) it("should terminate the group", async () => { @@ -744,13 +834,16 @@ describe("RandomBeacon - Relay", () => { }) it("should emit RelayEntryTimedOut event", async () => { - await expect(tx) + await expect(reportTx) .to.emit(randomBeacon, "RelayEntryTimedOut") .withArgs(1, 0) }) it("should clean up current relay request data", async () => { - await expect(tx).to.not.emit(randomBeacon, "RelayEntryRequested") + await expect(reportTx).to.not.emit( + randomBeacon, + "RelayEntryRequested" + ) expect(await randomBeacon.isRelayRequestInProgress()).to.be.false }) }) @@ -788,15 +881,16 @@ describe("RandomBeacon - Relay", () => { } ) - context("when token staking seize call fails", async () => { - let tokenStakingFake: FakeContract + // FIXME: Blocked by https://github.com/defi-wonderland/smock/issues/101 + context.skip("when token staking seize call fails", async () => { + let tokenStakingFake: FakeContract let tx: Promise before(async () => { await createSnapshot() tokenStakingFake = await fakeTokenStaking(randomBeacon) - tokenStakingFake.seize.reverts() + tokenStakingFake.seize.reverts("faked function revert") tx = randomBeacon.reportRelayEntryTimeout(membersIDs) }) @@ -846,7 +940,7 @@ describe("RandomBeacon - Relay", () => { await createSnapshot() await createGroup(randomBeacon, members) - await approveTestToken() + await approveTokenForFee() await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) }) @@ -856,7 +950,8 @@ describe("RandomBeacon - Relay", () => { context("when a group is active", () => { context("when provided signature is valid", () => { - let tx + let reportTx: ContractTransaction + let slashingTx: ContractTransaction before(async () => { await createSnapshot() @@ -865,9 +960,11 @@ describe("RandomBeacon - Relay", () => { notifier.address, blsData.secretKey ) - tx = await randomBeacon + reportTx = await randomBeacon .connect(notifier) .reportUnauthorizedSigning(notifierSignature, 0, membersIDs) + + slashingTx = await staking.processSlashing(membersAddresses.length) }) after(async () => { @@ -878,33 +975,61 @@ describe("RandomBeacon - Relay", () => { expect(await isGroupTerminated(0)).to.be.equal(true) }) - it("should call staking contract to seize the min stake", async () => { - await expect(tx) - .to.emit(staking, "Seized") + it("should reward the notifier", async () => { + await expect(reportTx) + .to.emit(staking, "NotifierRewarded") .withArgs( - to1e18(100000), - params.unauthorizedSigningNotificationRewardMultiplier, notifier.address, - membersAddresses + constants.tokenStakingNotificationReward + .mul(params.unauthorizedSigningNotificationRewardMultiplier) + .div(100) + .mul(membersIDs.length) ) }) + it("should slash unauthorized signing slashing amount for all group members", async () => { + for (let i = 0; i < membersAddresses.length; i++) { + const stakingProvider = + await randomBeacon.operatorToStakingProvider(membersAddresses[i]) + + await expect(slashingTx) + .to.emit(staking, "TokensSeized") + .withArgs( + stakingProvider, + params.unauthorizedSigningSlashingAmount, + false + ) + } + }) + it("should emit unauthorized signing slashing event", async () => { - await expect(tx) + await expect(reportTx) .to.emit(randomBeacon, "UnauthorizedSigningSlashed") - .withArgs(0, to1e18(100000), membersAddresses) + .withArgs( + 0, + params.unauthorizedSigningSlashingAmount, + membersAddresses + ) + }) + + it("should not emit UnauthorizedSigningSlashingFailed", async () => { + await expect(reportTx).to.not.emit( + randomBeacon, + "UnauthorizedSigningSlashingFailed" + ) }) }) - context("when token staking seize call fails", async () => { - let tokenStakingFake: FakeContract + // FIXME: Blocked by https://github.com/defi-wonderland/smock/issues/101 + context.skip("when token staking seize call fails", async () => { + let tokenStakingFake: FakeContract let tx: Promise before(async () => { await createSnapshot() tokenStakingFake = await fakeTokenStaking(randomBeacon) - tokenStakingFake.seize.reverts() + tokenStakingFake.seize.reverts("faked function revert") const notifierSignature = await bls.sign( notifier.address, @@ -1108,7 +1233,7 @@ describe("RandomBeacon - Relay", () => { await createSnapshot() // Assume claim sender is the first signing member. - claimSender = await ethers.getSigner(members[0].address) + claimSender = members[0].signer initialNonce = await randomBeacon.inactivityClaimNonce( groupId @@ -1870,9 +1995,9 @@ describe("RandomBeacon - Relay", () => { return groupData.terminated === true } - async function approveTestToken() { - await testToken.mint(requester.address, params.relayRequestFee) - await testToken + async function approveTokenForFee() { + await t.mint(requester.address, params.relayRequestFee) + await t .connect(requester) .approve(randomBeacon.address, params.relayRequestFee) } diff --git a/solidity/random-beacon/test/RandomBeaconGovernance.test.ts b/solidity/random-beacon/test/RandomBeaconGovernance.test.ts index cecd5b4996..f555d34e38 100644 --- a/solidity/random-beacon/test/RandomBeaconGovernance.test.ts +++ b/solidity/random-beacon/test/RandomBeaconGovernance.test.ts @@ -4,7 +4,11 @@ import { expect } from "chai" import { randomBeaconDeployment } from "./fixtures" import type { ContractTransaction, Signer } from "ethers" -import type { RandomBeacon, RandomBeaconGovernance } from "../typechain" +import type { + RandomBeacon, + RandomBeaconGovernance, + RandomBeaconGovernance__factory, +} from "../typechain" const { createSnapshot, restoreSnapshot } = helpers.snapshot @@ -93,7 +97,7 @@ describe("RandomBeaconGovernance", () => { initialAuthorizationDecreaseDelay ) - const RandomBeaconGovernance = await ethers.getContractFactory( + const RandomBeaconGovernance = await ethers.getContractFactory( "RandomBeaconGovernance" ) randomBeaconGovernance = await RandomBeaconGovernance.deploy( diff --git a/solidity/random-beacon/test/Reimbursable.test.ts b/solidity/random-beacon/test/Reimbursable.test.ts index af3cc53470..4ccdd512f3 100644 --- a/solidity/random-beacon/test/Reimbursable.test.ts +++ b/solidity/random-beacon/test/Reimbursable.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-expressions */ -import { ethers, waffle } from "hardhat" +import { ethers } from "hardhat" import { expect } from "chai" import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" diff --git a/solidity/random-beacon/test/fixtures/index.ts b/solidity/random-beacon/test/fixtures/index.ts index 32b5a27325..9ed64afa60 100644 --- a/solidity/random-beacon/test/fixtures/index.ts +++ b/solidity/random-beacon/test/fixtures/index.ts @@ -1,12 +1,16 @@ -import { ethers, helpers, getNamedAccounts } from "hardhat" +import { ethers, helpers, deployments } from "hardhat" +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import type { Contract } from "ethers" import type { SortitionPool, BeaconDkgValidator as DKGValidator, RandomBeaconStub, + TokenStaking, RandomBeaconGovernance, - StakingStub, + RandomBeaconStub__factory, + RandomBeaconGovernance__factory, + T, } from "../../typechain" const { to1e18 } = helpers.number @@ -15,8 +19,8 @@ export const constants = { groupSize: 64, groupThreshold: 33, offchainDkgTime: 72, // 5 * (1 + 5) + 2 * (1 + 10) + 20 - minimumStake: to1e18(100000), poolWeightDivisor: to1e18(1), + tokenStakingNotificationReward: to1e18(10000), // 10k T } export const dkgState = { @@ -48,8 +52,8 @@ export const params = { dkgMaliciousResultNotificationRewardMultiplier: 100, ineligibleOperatorNotifierReward: to1e18(200), unauthorizedSigningSlashingAmount: to1e18(100000), - minimumAuthorization: to1e18(100000), - authorizationDecreaseDelay: 0, + minimumAuthorization: to1e18(200000), + authorizationDecreaseDelay: 403200, reimbursmentPoolStaticGas: 41900, reimbursmentPoolMaxGasPrice: ethers.utils.parseUnits("20", "gwei"), } @@ -70,16 +74,6 @@ export async function blsDeployment(): Promise { return contracts } -export async function testTokenDeployment(): Promise { - const TestToken = await ethers.getContractFactory("TestToken") - const testToken = await TestToken.deploy() - await testToken.deployed() - - const contracts: DeployedContracts = { testToken } - - return contracts -} - export async function reimbursmentPoolDeployment(): Promise { const ReimbursementPool = await ethers.getContractFactory("ReimbursementPool") const reimbursementPool = await ReimbursementPool.deploy( @@ -94,20 +88,25 @@ export async function reimbursmentPoolDeployment(): Promise { } export async function randomBeaconDeployment(): Promise { - const deployer = await ethers.getSigner((await getNamedAccounts()).deployer) - - const { testToken } = await testTokenDeployment() + await deployments.fixture(["TokenStaking"]) + const t: T = await ethers.getContract("T") + const staking: TokenStaking = await ethers.getContract("TokenStaking") - const StakingStub = await ethers.getContractFactory("StakingStub") - const stakingStub: StakingStub = await StakingStub.deploy() + // TODO: Implement Hardhat deployment scripts and load deployed contracts, same + // as it's done above for T and TokenStaking. + const deployer: SignerWithAddress = await ethers.getNamedSigner("deployer") const SortitionPool = await ethers.getContractFactory("SortitionPool") const sortitionPool = (await SortitionPool.deploy( - stakingStub.address, - testToken.address, + staking.address, + t.address, constants.poolWeightDivisor )) as SortitionPool + const Authorization = await ethers.getContractFactory("BeaconAuthorization") + const authorization = await Authorization.deploy() + await authorization.deployed() + const BeaconDkg = await ethers.getContractFactory("BeaconDkg") const dkg = await BeaconDkg.deploy() await dkg.deployed() @@ -124,30 +123,39 @@ export async function randomBeaconDeployment(): Promise { )) as DKGValidator await dkgValidator.deployed() - const RandomBeacon = await ethers.getContractFactory("RandomBeaconStub", { - libraries: { - BLS: (await blsDeployment()).bls.address, - BeaconDkg: dkg.address, - BeaconInactivity: inactivity.address, - }, - }) + const RandomBeacon = + await ethers.getContractFactory( + "RandomBeaconStub", + { + libraries: { + BLS: (await blsDeployment()).bls.address, + BeaconAuthorization: authorization.address, + BeaconDkg: dkg.address, + BeaconInactivity: inactivity.address, + }, + } + ) + const randomBeacon: RandomBeaconStub = await RandomBeacon.deploy( sortitionPool.address, - testToken.address, - stakingStub.address, + t.address, + staking.address, dkgValidator.address ) await randomBeacon.deployed() + await staking.connect(deployer).approveApplication(randomBeacon.address) + await sortitionPool.connect(deployer).transferOwnership(randomBeacon.address) + await updateTokenStakingParams(t, staking, deployer) await setFixtureParameters(randomBeacon) const contracts: DeployedContracts = { sortitionPool, - stakingStub, + staking, randomBeacon, - testToken, + t, } return contracts @@ -156,9 +164,10 @@ export async function randomBeaconDeployment(): Promise { export async function testDeployment(): Promise { const contracts = await randomBeaconDeployment() - const RandomBeaconGovernance = await ethers.getContractFactory( - "RandomBeaconGovernance" - ) + const RandomBeaconGovernance = + await ethers.getContractFactory( + "RandomBeaconGovernance" + ) const randomBeaconGovernance: RandomBeaconGovernance = await RandomBeaconGovernance.deploy( contracts.randomBeacon.address, @@ -172,6 +181,23 @@ export async function testDeployment(): Promise { return { ...contracts, ...newContracts } } +async function updateTokenStakingParams( + t: T, + staking: TokenStaking, + deployer: SignerWithAddress +) { + // initialNotifierTreasury should be configured high enough to execute all the + // slashing in test suites. + const initialNotifierTreasury = to1e18(9_000_000) // 9MM T + await t.connect(deployer).approve(staking.address, initialNotifierTreasury) + await staking + .connect(deployer) + .pushNotificationReward(initialNotifierTreasury) + await staking + .connect(deployer) + .setNotificationReward(constants.tokenStakingNotificationReward) +} + async function setFixtureParameters(randomBeacon: RandomBeaconStub) { await randomBeacon.updateAuthorizationParameters( params.minimumAuthorization, diff --git a/solidity/random-beacon/test/mocks/staking.ts b/solidity/random-beacon/test/mocks/staking.ts index a62b113536..ab4d053cbb 100644 --- a/solidity/random-beacon/test/mocks/staking.ts +++ b/solidity/random-beacon/test/mocks/staking.ts @@ -1,18 +1,15 @@ import { smock } from "@defi-wonderland/smock" import type { FakeContract } from "@defi-wonderland/smock" -import type { IRandomBeaconStaking, RandomBeacon } from "../../typechain" +import type { RandomBeacon, TokenStaking } from "../../typechain" // eslint-disable-next-line import/prefer-default-export export async function fakeTokenStaking( randomBeacon: RandomBeacon -): Promise> { - const tokenStaking = await smock.fake( - "IRandomBeaconStaking", - { - address: await randomBeacon.callStatic.staking(), - } - ) +): Promise> { + const tokenStaking = await smock.fake("TokenStaking", { + address: await randomBeacon.callStatic.staking(), + }) return tokenStaking } diff --git a/solidity/random-beacon/test/system/e2e.test.ts b/solidity/random-beacon/test/system/e2e.test.ts index 6b551c6a36..837169a8e3 100644 --- a/solidity/random-beacon/test/system/e2e.test.ts +++ b/solidity/random-beacon/test/system/e2e.test.ts @@ -1,12 +1,6 @@ /* eslint-disable no-await-in-loop */ -import { - ethers, - waffle, - helpers, - getUnnamedAccounts, - getNamedAccounts, -} from "hardhat" +import { ethers, waffle, helpers } from "hardhat" import { expect } from "chai" import { @@ -24,7 +18,7 @@ import blsData from "../data/bls" import { registerOperators } from "../utils/operators" import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" -import type { RandomBeacon, RandomBeaconStub, TestToken } from "../../typechain" +import type { RandomBeacon, RandomBeaconStub, T } from "../../typechain" const ZERO_ADDRESS = ethers.constants.AddressZero @@ -35,17 +29,21 @@ const { keccak256 } = ethers.utils const fixture = async () => { const contracts = await randomBeaconDeployment() + // Accounts offset provided to slice getUnnamedSigners have to include number + // of unnamed accounts that were already used. await registerOperators( contracts.randomBeacon as RandomBeacon, - (await getUnnamedAccounts()).slice(1, 1 + constants.groupSize) + contracts.t as T, + constants.groupSize, + 1 ) const randomBeacon = contracts.randomBeacon as RandomBeaconStub & RandomBeacon - const testToken = contracts.testToken as TestToken + const t = contracts.t as T return { randomBeacon, - testToken, + t, } } @@ -72,17 +70,17 @@ describe("System -- e2e", () => { ] let randomBeacon: RandomBeacon - let testToken: TestToken + let t: T let requester: SignerWithAddress let owner: SignerWithAddress before(async () => { const contracts = await waffle.loadFixture(fixture) - owner = await ethers.getSigner((await getNamedAccounts()).deployer) - requester = await ethers.getSigner((await getUnnamedAccounts())[1]) + owner = await ethers.getNamedSigner("deployer") + ;[requester] = await ethers.getUnnamedSigners() randomBeacon = contracts.randomBeacon - testToken = contracts.testToken + t = contracts.t await randomBeacon .connect(owner) @@ -140,7 +138,7 @@ describe("System -- e2e", () => { .approveDkgResult(dkgResult.dkgResult) for (let i = 1; i <= 14; i++) { - await approveTestToken(requester) + await approveTokenForFee(requester) await randomBeacon.connect(requester).requestRelayEntry(ZERO_ADDRESS) const txSubmitRelayEntry = await randomBeacon @@ -197,10 +195,8 @@ describe("System -- e2e", () => { }) }) - async function approveTestToken(_requester) { - await testToken.mint(_requester.address, relayRequestFee) - await testToken - .connect(_requester) - .approve(randomBeacon.address, relayRequestFee) + async function approveTokenForFee(_requester) { + await t.mint(_requester.address, relayRequestFee) + await t.connect(_requester).approve(randomBeacon.address, relayRequestFee) } }) diff --git a/solidity/random-beacon/test/utils/dkg.ts b/solidity/random-beacon/test/utils/dkg.ts index 86e2b7f2f4..4eadebc372 100644 --- a/solidity/random-beacon/test/utils/dkg.ts +++ b/solidity/random-beacon/test/utils/dkg.ts @@ -123,7 +123,7 @@ export async function signAndSubmitArbitraryDkgResult( ) ) - const submitter = await ethers.getSigner(signers[submitterIndex - 1].address) + const submitter = signers[submitterIndex - 1].signer const transaction = await randomBeacon .connect(submitter) @@ -190,7 +190,7 @@ export async function signAndSubmitUnrecoverableDkgResult( ) ) - const submitter = await ethers.getSigner(signers[submitterIndex - 1].address) + const submitter = signers[submitterIndex - 1].signer const transaction = await randomBeacon .connect(submitter) @@ -219,7 +219,7 @@ export async function signDkgResult( const signingMembersIndices: number[] = [] const signatures: string[] = [] for (let i = 0; i < signers.length; i++) { - const { id, address } = signers[i] + const { id, signer: ethersSigner } = signers[i] members.push(id) if (signatures.length === numberOfSignatures) { @@ -231,7 +231,6 @@ export async function signDkgResult( signingMembersIndices.push(signerIndex) - const ethersSigner = await ethers.getSigner(address) const signature = await ethersSigner.signMessage( ethers.utils.arrayify(resultHash) ) diff --git a/solidity/random-beacon/test/utils/groups.ts b/solidity/random-beacon/test/utils/groups.ts index de3b6f4a29..c952672b25 100644 --- a/solidity/random-beacon/test/utils/groups.ts +++ b/solidity/random-beacon/test/utils/groups.ts @@ -44,10 +44,14 @@ export async function selectGroup( ) const addresses = await sortitionPool.getIDOperators(identifiers) - return identifiers.map((identifier, i) => ({ - id: identifier, - address: addresses[i], - })) + return Promise.all( + identifiers.map( + async (identifier, i): Promise => ({ + id: identifier, + signer: await ethers.getSigner(addresses[i]), + }) + ) + ) } export function hashUint32Array(arrayToHash: BigNumberish[]): string { diff --git a/solidity/random-beacon/test/utils/inacvitity.ts b/solidity/random-beacon/test/utils/inacvitity.ts index 96f7ed9e97..c243ee072b 100644 --- a/solidity/random-beacon/test/utils/inacvitity.ts +++ b/solidity/random-beacon/test/utils/inacvitity.ts @@ -31,8 +31,7 @@ export async function signOperatorInactivityClaim( signingMembersIndices.push(signerIndex) - // eslint-disable-next-line no-await-in-loop - const ethersSigner = await ethers.getSigner(signers[i].address) + const ethersSigner = signers[i].signer // eslint-disable-next-line no-await-in-loop const signature = await ethersSigner.signMessage( ethers.utils.arrayify(messageHash) diff --git a/solidity/random-beacon/test/utils/operators.ts b/solidity/random-beacon/test/utils/operators.ts index b39e756cfb..8b2273a060 100644 --- a/solidity/random-beacon/test/utils/operators.ts +++ b/solidity/random-beacon/test/utils/operators.ts @@ -2,45 +2,112 @@ import { ethers } from "hardhat" -import { constants } from "../fixtures" +import { params } from "../fixtures" +import { testConfig } from "../../hardhat.config" -import type { Address } from "hardhat-deploy/types" -import type { BigNumber } from "ethers" -import type { RandomBeacon } from "../../typechain" +import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" +import type { BigNumber, BigNumberish } from "ethers" +import type { + RandomBeacon, + RandomBeaconStub, + SortitionPool, + T, + TokenStaking, +} from "../../typechain" export type OperatorID = number -export type Operator = { id: OperatorID; address: Address } +export type Operator = { id: OperatorID; signer: SignerWithAddress } export async function registerOperators( randomBeacon: RandomBeacon, - addresses: Address[], - stakeAmount: BigNumber = constants.minimumStake + t: T, + numberOfOperators = testConfig.operatorsCount, + unnamedSignersOffset = testConfig.nonStakingAccountsCount, + stakeAmount: BigNumber = params.minimumAuthorization ): Promise { const operators: Operator[] = [] - const sortitionPool = await ethers.getContractAt( + const sortitionPool: SortitionPool = await ethers.getContractAt( "SortitionPool", await randomBeacon.sortitionPool() ) - const staking = await ethers.getContractAt( - "StakingStub", + const staking: TokenStaking = await ethers.getContractAt( + "TokenStaking", await randomBeacon.staking() ) - for (let i = 0; i < addresses.length; i++) { - const address = addresses[i] + const signers = (await ethers.getUnnamedSigners()).slice(unnamedSignersOffset) - await staking.setStake(address, stakeAmount) + // We use unique accounts for each staking role for each operator. + if (signers.length < numberOfOperators * 5) { + throw new Error( + "not enough unnamed signers; update hardhat network's configuration account count" + ) + } + + for (let i = 0; i < numberOfOperators; i++) { + const owner: SignerWithAddress = signers[i] + const stakingProvider: SignerWithAddress = + signers[1 * numberOfOperators + i] + const operator: SignerWithAddress = signers[2 * numberOfOperators + i] + const beneficiary: SignerWithAddress = signers[3 * numberOfOperators + i] + const authorizer: SignerWithAddress = signers[4 * numberOfOperators + i] + + await stake( + t, + staking, + randomBeacon, + owner, + stakingProvider, + stakeAmount, + beneficiary, + authorizer + ) await randomBeacon - .connect(await ethers.getSigner(address)) - .registerOperator() + .connect(stakingProvider) + .registerOperator(operator.address) - const id = await sortitionPool.getOperatorID(address) + await randomBeacon.connect(operator).joinSortitionPool() - operators.push({ id, address }) + const id = await sortitionPool.getOperatorID(operator.address) + + operators.push({ id, signer: operator }) } return operators } + +export async function stake( + t: T, + staking: TokenStaking, + randomBeacon: RandomBeacon | RandomBeaconStub, + owner: SignerWithAddress, + stakingProvider: SignerWithAddress, + stakeAmount: BigNumberish, + beneficiary = stakingProvider, + authorizer = stakingProvider +): Promise { + const deployer: SignerWithAddress = await ethers.getNamedSigner("deployer") + + await t.connect(deployer).mint(owner.address, stakeAmount) + await t.connect(owner).approve(staking.address, stakeAmount) + + await staking + .connect(owner) + .stake( + stakingProvider.address, + beneficiary.address, + authorizer.address, + stakeAmount + ) + + await staking + .connect(authorizer) + .increaseAuthorization( + stakingProvider.address, + randomBeacon.address, + stakeAmount + ) +} diff --git a/solidity/random-beacon/yarn.lock b/solidity/random-beacon/yarn.lock index cebd4fac86..fe2708b511 100644 --- a/solidity/random-beacon/yarn.lock +++ b/solidity/random-beacon/yarn.lock @@ -605,10 +605,18 @@ deepmerge "^4.2.2" untildify "^4.0.0" -"@keep-network/sortition-pools@1.2.0-dev.24": - version "1.2.0-dev.24" - resolved "https://registry.yarnpkg.com/@keep-network/sortition-pools/-/sortition-pools-1.2.0-dev.24.tgz#ed2bd34297ba3832d8981f2f5705489caece2115" - integrity sha512-k6PgZ3Svv4nCAP3bE3jfPoz7V6WUf9f8qKSrlMUGB6TkrgoSwg7I9mrC5UCawTrd3QL86FI/3hZt+4FJvZt74w== +"@keep-network/keep-core@>1.8.0-dev <1.8.0-pre": + version "1.8.0-dev.11" + resolved "https://registry.yarnpkg.com/@keep-network/keep-core/-/keep-core-1.8.0-dev.11.tgz#cf607c6b9f86b545d8110ea4857b2eef5f3de737" + integrity sha512-NWsG0RqsJm+ZTbSUTWXgmJe6tSyNmHVhx7tQO/7d3/A31hEpbeJeC2H8ro7Pj88M6JDHSvor7svn8bL0KlIy1A== + dependencies: + "@openzeppelin/upgrades" "^2.7.2" + openzeppelin-solidity "2.4.0" + +"@keep-network/sortition-pools@^2.0.0-pre.6": + version "2.0.0-pre.6" + resolved "https://registry.yarnpkg.com/@keep-network/sortition-pools/-/sortition-pools-2.0.0-pre.6.tgz#72ca66cc0c476a21644d9b2342f7136268c802eb" + integrity sha512-i+naD1W10pxCRjzJ63yeNJgpokpWzr7eTDI4TRCT1opIRyYCUPa6VNF+U3WGGToM5PtgrBHIe1TZbL7nx8LTSw== dependencies: "@openzeppelin/contracts" "^4.3.2" "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" @@ -655,10 +663,10 @@ safe-buffer "^5.1.1" util.promisify "^1.0.0" -"@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-ethers@npm:hardhat-deploy-ethers": + version "0.3.0-beta.13" + resolved "https://registry.yarnpkg.com/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.3.0-beta.13.tgz#b96086ff768ddf69928984d5eb0a8d78cfca9366" + integrity sha512-PdWVcKB9coqWV1L7JTpfXRCI91Cgwsm7KLmBcwZ8f0COSm1xtABHZTyz3fvF6p42cTnz1VM0QnfDvMFlIRkSNw== "@nomiclabs/hardhat-waffle@^2.0.1": version "2.0.1" @@ -668,11 +676,50 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" -"@openzeppelin/contracts@^4.1.0", "@openzeppelin/contracts@^4.3.2", "@openzeppelin/contracts@^4.4.2": +"@openzeppelin/contracts-upgradeable@^4.4": + version "4.5.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz#90d9e47bacfd8693bfad0ac8a394645575528d05" + integrity sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA== + +"@openzeppelin/contracts@^4.1.0", "@openzeppelin/contracts@^4.4.2": version "4.4.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.4.2.tgz#4e889c9c66e736f7de189a53f8ba5b8d789425c2" integrity sha512-NyJV7sJgoGYqbtNUWgzzOGW4T6rR19FmX1IJgXGdapGPWsuMelGJn9h03nos0iqfforCbCB0iYIR0MtIuIFLLw== +"@openzeppelin/contracts@^4.3.2", "@openzeppelin/contracts@^4.4": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.5.0.tgz#3fd75d57de172b3743cdfc1206883f56430409cc" + integrity sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA== + +"@openzeppelin/upgrades@^2.7.2": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades/-/upgrades-2.8.0.tgz#8086ab9c99d9f8dac7205030b0f9e7e4a280c4a3" + integrity sha512-LzjTQPeljPsgHDPdZyH9cMCbIHZILgd2cpNcYEkdsC2IylBYRHShlbEDXJV9snnqg9JWfzPiKIqyj3XVliwtqQ== + dependencies: + "@types/cbor" "^2.0.0" + axios "^0.18.0" + bignumber.js "^7.2.0" + cbor "^4.1.5" + chalk "^2.4.1" + ethers "^4.0.20" + glob "^7.1.3" + lodash "^4.17.15" + semver "^5.5.1" + spinnies "^0.4.2" + truffle-flattener "^1.4.0" + web3 "1.2.2" + web3-eth "1.2.2" + web3-eth-contract "1.2.2" + web3-utils "1.2.2" + +"@resolver-engine/core@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.2.1.tgz#0d71803f6d3b8cb2e9ed481a1bf0ca5f5256d0c0" + integrity sha512-nsLQHmPJ77QuifqsIvqjaF5B9aHnDzJjp73Q1z6apY3e9nqYrx4Dtowhpsf7Jwftg/XzVDEMQC+OzUBNTS+S1A== + dependencies: + debug "^3.1.0" + request "^2.85.0" + "@resolver-engine/core@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" @@ -682,6 +729,14 @@ is-url "^1.2.4" request "^2.85.0" +"@resolver-engine/fs@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@resolver-engine/fs/-/fs-0.2.1.tgz#f98a308d77568cc02651d03636f46536b941b241" + integrity sha512-7kJInM1Qo2LJcKyDhuYzh9ZWd+mal/fynfL9BNjWOiTcOpX+jNfqb/UmGUqros5pceBITlWGqS4lU709yHFUbg== + dependencies: + "@resolver-engine/core" "^0.2.1" + debug "^3.1.0" + "@resolver-engine/fs@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@resolver-engine/fs/-/fs-0.3.3.tgz#fbf83fa0c4f60154a82c817d2fe3f3b0c049a973" @@ -690,6 +745,15 @@ "@resolver-engine/core" "^0.3.3" debug "^3.1.0" +"@resolver-engine/imports-fs@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports-fs/-/imports-fs-0.2.2.tgz#5a81ef3285dbf0411ab3b15205080a1ad7622d9e" + integrity sha512-gFCgMvCwyppjwq0UzIjde/WI+yDs3oatJhozG9xdjJdewwtd7LiF0T5i9lrHAUtqrQbqoFE4E+ZMRVHWpWHpKQ== + dependencies: + "@resolver-engine/fs" "^0.2.1" + "@resolver-engine/imports" "^0.2.2" + debug "^3.1.0" + "@resolver-engine/imports-fs@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@resolver-engine/imports-fs/-/imports-fs-0.3.3.tgz#4085db4b8d3c03feb7a425fbfcf5325c0d1e6c1b" @@ -699,6 +763,15 @@ "@resolver-engine/imports" "^0.3.3" debug "^3.1.0" +"@resolver-engine/imports@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports/-/imports-0.2.2.tgz#d3de55a1bb5f3beb7703fdde743298f321175843" + integrity sha512-u5/HUkvo8q34AA+hnxxqqXGfby5swnH0Myw91o3Sm2TETJlNKXibFGSKBavAH+wvWdBi4Z5gS2Odu0PowgVOUg== + dependencies: + "@resolver-engine/core" "^0.2.1" + debug "^3.1.0" + hosted-git-info "^2.6.0" + "@resolver-engine/imports@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@resolver-engine/imports/-/imports-0.3.3.tgz#badfb513bb3ff3c1ee9fd56073e3144245588bcc" @@ -814,6 +887,13 @@ dependencies: antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -857,6 +937,16 @@ dependencies: "@openzeppelin/contracts" "^4.1.0" +"@threshold-network/solidity-contracts@>1.1.0-dev <1.1.0-ropsten": + version "1.1.0-dev.8" + resolved "https://registry.yarnpkg.com/@threshold-network/solidity-contracts/-/solidity-contracts-1.1.0-dev.8.tgz#5c8ed6e9dab26823f25c319a5ade167c6bb8efa3" + integrity sha512-7xsjMIO3jtVDOB3X+tY6rEy9qViGHLwxyNEdgYH85BzpOdXvL3tlmwVnAn0k1fQyRhGLrUOtS56Z6fsDCxyRRg== + dependencies: + "@keep-network/keep-core" ">1.8.0-dev <1.8.0-pre" + "@openzeppelin/contracts" "^4.4" + "@openzeppelin/contracts-upgradeable" "^4.4" + "@thesis/solidity-contracts" "github:thesis/solidity-contracts#4985bcf" + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -911,13 +1001,20 @@ dependencies: "@types/node" "*" -"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== dependencies: "@types/node" "*" +"@types/cbor@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/cbor/-/cbor-2.0.0.tgz#c627afc2ee22f23f2337fecb34628a4f97c6afbb" + integrity sha1-xievwu4i8j8jN/7LNGKKT5fGr7s= + dependencies: + "@types/node" "*" + "@types/chai@*", "@types/chai@^4.2.22": version "4.2.22" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.22.tgz#47020d7e4cf19194d43b5202f35f75bd2ad35ce7" @@ -987,11 +1084,11 @@ form-data "^3.0.0" "@types/node@*": - version "16.10.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.2.tgz#5764ca9aa94470adb4e1185fe2e9f19458992b2e" - integrity sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ== + version "17.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" + integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== -"@types/node@^10.0.3": +"@types/node@^10.0.3", "@types/node@^10.12.18", "@types/node@^10.3.2": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== @@ -1001,6 +1098,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.25.tgz#882bea2ca0d2ec22126b92b4dd2dc24b35a07469" integrity sha512-hcTWqk7DR/HrN9Xe7AlJwuCaL13Vcd9/g/T54YrJz4Q3ESM5mr33YCzW2bOfzSIc3aZMeGBvbLGvgN6mIJ0I5Q== +"@types/node@^12.6.1": + version "12.20.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188" + integrity sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg== + "@types/node@^16.10.5": version "16.10.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.5.tgz#7fe4123b061753f1a58a6cd077ff0bb069ee752d" @@ -1202,13 +1304,13 @@ abstract-leveldown@~6.2.1: level-supports "~1.0.0" xtend "~4.0.0" -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" acorn-jsx@^5.0.0, acorn-jsx@^5.3.1: version "5.3.2" @@ -1310,9 +1412,9 @@ ansi-regex@^3.0.0: integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== ansi-regex@^5.0.1: version "5.0.1" @@ -1348,6 +1450,11 @@ antlr4ts@^0.5.0-alpha.4: resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== +any-promise@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@~3.1.1, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -1475,9 +1582,9 @@ asn1.js@^5.2.0: safer-buffer "^2.1.0" asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" @@ -1577,6 +1684,14 @@ axe-core@^4.0.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.3.tgz#b55cd8e8ddf659fe89b064680e1c6a4dceab0325" integrity sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA== +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + axios@^0.21.1: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -2120,7 +2235,14 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.8: +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base-x@^3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== @@ -2157,10 +2279,15 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +bignumber.js@^7.2.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + bignumber.js@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" - integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + version "9.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== binary-extensions@^2.0.0: version "2.2.0" @@ -2192,10 +2319,18 @@ bip66@^1.1.5: dependencies: safe-buffer "^5.0.1" +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + blakejs@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" - integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== bluebird@^3.5.0, bluebird@^3.5.2: version "3.7.2" @@ -2207,31 +2342,36 @@ bn.js@4.11.6: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.8.0: +bn.js@4.11.8: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^4.8.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -body-parser@1.19.0, body-parser@^1.16.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.19.2, body-parser@^1.16.0: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== dependencies: - bytes "3.1.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "1.7.2" + http-errors "1.8.1" iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + qs "6.9.7" + raw-body "2.4.3" + type-is "~1.6.18" brace-expansion@^1.1.7: version "1.1.11" @@ -2352,6 +2492,29 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2394,6 +2557,11 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + bytewise-core@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/bytewise-core/-/bytewise-core-1.2.3.tgz#3fb410c7e91558eb1ab22a82834577aa6bd61d42" @@ -2497,6 +2665,16 @@ caseless@^0.12.0, caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +cbor@^4.1.5: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-4.3.0.tgz#0217c1cadd067d9112f44336dca07e72020bb804" + integrity sha512-CvzaxQlaJVa88sdtTWvLJ++MbdtPHtZOBBNjm7h3YKUHILMs9nQyD4AC6hvFZy7GBVB3I6bRibJcxeHydyT2IQ== + dependencies: + bignumber.js "^9.0.0" + commander "^3.0.0" + json-text-sequence "^0.1" + nofilter "^1.0.3" + chai@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" @@ -2640,6 +2818,13 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + cli-table3@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -2732,7 +2917,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@^1.1.2, colors@^1.4.0: +colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -2783,11 +2968,16 @@ commander@2.18.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== -commander@3.0.2: +commander@3.0.2, commander@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== +commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2813,12 +3003,12 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-hash@^2.5.2: version "2.5.2" @@ -2846,10 +3036,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== cookie@^0.4.1: version "0.4.1" @@ -3039,6 +3229,13 @@ debug@4, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: dependencies: ms "2.1.2" +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -3063,6 +3260,59 @@ decompress-response@^3.2.0, decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -3164,6 +3414,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delimit-stream@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" + integrity sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs= + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3278,7 +3533,17 @@ electron-to-chromium@^1.3.47: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.859.tgz#4e0abc918e1c22b306ba13b4c3649f78295f5937" integrity sha512-gXRXKNWedfdiKIzwr0Mg/VGCvxXzy+4SuK9hp1BDvfbCwx0O5Ot+2f4CoqQkqEJ3Zj/eAV/GoAFgBVFgkBLXuQ== -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: +elliptic@6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" + integrity sha1-VILZZG1UvLif19mU/J4ulWiHbj8= + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -3344,7 +3609,7 @@ encoding@^0.1.11: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -3413,15 +3678,15 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.53" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + version "0.10.59" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.59.tgz#71038939730eb6f4f165f1421308fb60be363bc6" + integrity sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw== dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.3" - next-tick "~1.0.0" + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" -es6-iterator@~2.0.3: +es6-iterator@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= @@ -3430,7 +3695,7 @@ es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-symbol@^3.1.1, es6-symbol@~3.1.3: +es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== @@ -3830,6 +4095,15 @@ eth-json-rpc-middleware@^1.5.0: promise-to-callback "^1.0.0" tape "^4.6.3" +eth-lib@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + integrity sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco= + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + eth-lib@0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" @@ -4184,7 +4458,23 @@ ethereumjs-wallet@0.6.5: utf8 "^3.0.0" uuid "^3.3.2" -ethers@^4.0.40: +ethers@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.3.tgz#15bef14e57e94ecbeb7f9b39dd0a4bd435bc9066" + integrity sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog== + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.3" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^4.0.20, ethers@^4.0.40: version "4.0.49" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== @@ -4256,6 +4546,11 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter3@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" @@ -4293,16 +4588,16 @@ expand-brackets@^2.1.4: to-regex "^3.0.1" express@^4.14.0: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.19.2" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.4.2" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" @@ -4316,13 +4611,13 @@ express@^4.14.0: on-finished "~2.3.0" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.9.7" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" statuses "~1.5.0" type-is "~1.6.18" utils-merge "1.0.1" @@ -4384,9 +4679,9 @@ extsprintf@1.3.0: integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fake-merkle-patricia-tree@^1.0.1: version "1.0.1" @@ -4433,6 +4728,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + fetch-ponyfill@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" @@ -4461,6 +4763,21 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= + +file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha1-LdvqfHP/42No365J3DOMBYwritY= + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -4594,6 +4911,13 @@ fmix@^0.1.0: dependencies: imul "^1.0.0" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + follow-redirects@^1.12.1, follow-redirects@^1.14.0: version "1.14.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" @@ -4679,6 +5003,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -4830,6 +5159,14 @@ get-port@^3.1.0: resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -4986,7 +5323,12 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + +graceful-fs@^4.1.11, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -5009,24 +5351,30 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -hardhat-contract-sizer@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.1.1.tgz#d861bfac8dff00cca859e14c5a4843367dd0f068" - integrity sha512-QgfuwdUkKT7Ugn6Zja26Eie7h6OLcBfWBewaaQtYMCzyglNafQPgUIznN2C42/iFmGrlqFPbqv4B98Iev89KSQ== +hardhat-contract-sizer@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.5.1.tgz#cb0b8dd32593b7a28c8d96ecde04841292bbd603" + integrity sha512-28yRb73e30aBVaZOOHTlHZFIdIasA/iFunIehrUviIJTubvdQjtSiQUo2wexHFtt71mQeMPP8qjw2sdbgatDnQ== dependencies: + chalk "^4.0.0" cli-table3 "^0.6.0" - colors "^1.4.0" -hardhat-deploy@^0.9.1: - version "0.9.3" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.9.3.tgz#cf9a7806c0a86a6f7e9352fc558b26c079983496" - integrity sha512-0sxxQoxcA1+LSVmmLwp9empK4Pz5tjr92JvfcobojBz35DSpp0o3f0f/tXocYWaGbOrms3eQJ8OX5WVsmcnN4Q== +hardhat-dependency-compiler@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/hardhat-dependency-compiler/-/hardhat-dependency-compiler-1.1.2.tgz#02867b7c6dd3de4924d9d3d6593feab8408f1eeb" + integrity sha512-LVnsPSZnGvzWVvlpewlkPKlPtFP/S9V41RC1fd/ygZc4jkG8ubNlfE82nwiGw5oPueHSmFi6TACgmyrEOokK8w== + +hardhat-deploy@^0.11.2: + version "0.11.4" + resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.4.tgz#39b06d3b0ad25195071cc1f2f71649b1f9f030d0" + integrity sha512-BNMwWqaxrwb8XKrYzmCwnUzOSKzicUBk+fwd28doUNoAGFFh8kpoypkcHMzKDVdLhnamAardcfqJet73zrZoTA== dependencies: "@ethersproject/abi" "^5.4.0" "@ethersproject/abstract-signer" "^5.4.1" "@ethersproject/address" "^5.4.0" "@ethersproject/bignumber" "^5.4.1" "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" "@ethersproject/contracts" "^5.4.1" "@ethersproject/providers" "^5.4.4" "@ethersproject/solidity" "^5.4.0" @@ -5261,27 +5609,27 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== +http-errors@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" - inherits "2.0.3" + inherits "2.0.4" setprototypeof "1.1.1" statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@1.7.3, http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" inherits "2.0.4" - setprototypeof "1.1.1" + setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + toidentifier "1.0.1" http-https@^1.0.0: version "1.0.0" @@ -5402,11 +5750,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - inquirer@^6.2.2: version "6.5.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" @@ -5513,7 +5856,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: +is-buffer@^2.0.2, 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== @@ -5647,6 +5990,11 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -5706,7 +6054,7 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== -is-stream@^1.0.0, is-stream@^1.0.1: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -5894,10 +6242,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -5916,6 +6264,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json-text-sequence@^0.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2" + integrity sha1-py8hfcSvxGKf/1/rME3BvVGi89I= + dependencies: + delimit-stream "0.1.0" + json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -5957,13 +6312,13 @@ jsonify@~0.0.0: integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" extsprintf "1.3.0" - json-schema "0.2.3" + json-schema "0.4.0" verror "1.10.0" "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0: @@ -6430,6 +6785,13 @@ ltgt@~2.1.1: resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" integrity sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ= +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -6607,17 +6969,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.50.0: - version "1.50.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" - integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.33" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb" - integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g== +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - mime-db "1.50.0" + mime-db "1.52.0" mime@1.6.0: version "1.6.0" @@ -6629,6 +6991,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -6651,18 +7018,30 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -6698,13 +7077,20 @@ mkdirp@*, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@0.5.5, mkdirp@^0.5.1, mkdirp@^0.5.5: +mkdirp@0.5.5, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mnemonist@^0.38.0: version "0.38.4" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.4.tgz#5d2f2dc4386aef78bfadeea60ce704dcf0ef8f3d" @@ -6762,7 +7148,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -6853,15 +7239,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -next-tick@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== nice-try@^1.0.4: version "1.0.5" @@ -6906,6 +7292,11 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== +nofilter@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -6944,7 +7335,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4, object-assign@^4.0.0, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -7092,6 +7483,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -7100,6 +7498,11 @@ open@^7.4.2: is-docker "^2.0.0" is-wsl "^2.1.1" +openzeppelin-solidity@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.4.0.tgz#5f0a7b30571c45493449166e57b947203415349d" + integrity sha512-533gc5jkspxW5YT0qJo02Za5q1LHwXK9CJCc48jNj/22ncNM/3M/3JfWLqfpB90uqLwOKOovpl0JfaMQTR+gXQ== + optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7225,9 +7628,9 @@ parse-cache-control@^1.0.1: integrity sha1-juqz5U+laSD+Fro493+iGqzC104= parse-headers@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" - integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== + version "2.0.5" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== parse-json@^2.2.0: version "2.2.0" @@ -7375,6 +7778,11 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -7534,7 +7942,7 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -proxy-addr@~2.0.5: +proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -7640,10 +8048,10 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== qs@^6.4.0, qs@^6.7.0, qs@^6.9.4: version "6.10.1" @@ -7653,9 +8061,9 @@ qs@^6.4.0, qs@^6.7.0, qs@^6.9.4: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^5.0.1: version "5.1.1" @@ -7696,13 +8104,13 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "1.8.1" iconv-lite "0.4.24" unpipe "1.0.0" @@ -7765,7 +8173,7 @@ readable-stream@^1.0.33: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8038,6 +8446,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + resumer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" @@ -8084,13 +8500,20 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: +rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.4: version "2.2.6" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== dependencies: bn.js "^4.11.1" +rlp@^2.2.3: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + run-async@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -8122,16 +8545,16 @@ rxjs@^7.2.0: dependencies: tslib "^2.1.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-event-emitter@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" @@ -8151,6 +8574,11 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +scrypt-js@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" + integrity sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q= + scrypt-js@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -8161,6 +8589,13 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== +"scrypt-shim@github:web3-js/scrypt-shim": + version "0.1.0" + resolved "https://codeload.github.com/web3-js/scrypt-shim/tar.gz/aafdadda13e660e25e1c525d1f5b2443f5eb1ebb" + dependencies: + scryptsy "^2.1.0" + semver "^6.3.0" + scryptsy@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" @@ -8168,6 +8603,11 @@ scryptsy@^1.2.1: dependencies: pbkdf2 "^3.0.3" +scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + secp256k1@^3.0.1: version "3.8.0" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" @@ -8183,11 +8623,11 @@ secp256k1@^3.0.1: safe-buffer "^5.1.2" secp256k1@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== dependencies: - elliptic "^6.5.2" + elliptic "^6.5.4" node-addon-api "^2.0.0" node-gyp-build "^4.2.0" @@ -8196,6 +8636,13 @@ seedrandom@3.0.1: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" integrity sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg== +seek-bzip@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" + integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== + dependencies: + commander "^2.8.1" + semaphore-async-await@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" @@ -8228,10 +8675,10 @@ semver@~5.4.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" depd "~1.1.2" @@ -8240,22 +8687,22 @@ send@0.17.1: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "1.8.1" mime "1.6.0" - ms "2.1.1" + ms "2.1.3" on-finished "~2.3.0" range-parser "~1.2.1" statuses "~1.5.0" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.17.2" servify@^0.1.12: version "0.1.12" @@ -8303,6 +8750,11 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -8353,9 +8805,9 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" - integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-concat@^1.0.0: version "1.0.1" @@ -8580,6 +9032,15 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== +spinnies@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/spinnies/-/spinnies-0.4.3.tgz#2ea0ad148e78353ddf621dec3951a6f4c3cbf66e" + integrity sha512-TTA2vWXrXJpfThWAl2t2hchBnCMI1JM5Wmb2uyI7Zkefdw/xO98LDy6/SBYwQPiYXL3swx3Eb44ZxgoS8X5wpA== + dependencies: + chalk "^2.4.2" + cli-cursor "^3.0.0" + strip-ansi "^5.2.0" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8593,9 +9054,9 @@ sprintf-js@~1.0.2: integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -8783,6 +9244,13 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + strip-hex-prefix@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" @@ -8826,6 +9294,24 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +swarm-js@0.1.39: + version "0.1.39" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8" + integrity sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + decompress "^4.0.0" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request-promise "^0.1.2" + swarm-js@^0.1.40: version "0.1.40" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" @@ -8912,6 +9398,19 @@ tape@^4.6.3: string.prototype.trim "~1.2.4" through "~2.3.8" +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + tar@^4.0.2: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -8968,7 +9467,7 @@ through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6, through@~2.3.4, through@~2.3.8: +through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -8992,6 +9491,11 @@ tmp@0.1.0: dependencies: rimraf "^2.6.3" +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" @@ -9039,6 +9543,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -9062,6 +9571,17 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +truffle-flattener@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.6.0.tgz#abb64488b711e6cca0a9d3e449f6a85e35964c5d" + integrity sha512-scS5Bsi4CZyvlrmD4iQcLHTiG2RQFUXVheTgWeH6PuafmI+Lk5U87Es98loM3w3ImqC9/fPHq+3QIXbcPuoJ1Q== + dependencies: + "@resolver-engine/imports-fs" "^0.2.2" + "@solidity-parser/parser" "^0.14.1" + find-up "^2.1.0" + mkdirp "^1.0.4" + tsort "0.0.1" + ts-command-line-args@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz#fd6913e542099012c0ffb2496126a8f38305c7d6" @@ -9208,7 +9728,7 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -9222,9 +9742,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + version "2.6.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" + integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== typechain@^3.0.0: version "3.0.0" @@ -9319,6 +9839,14 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbzip2-stream@^1.0.9: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + underscore@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" @@ -9506,6 +10034,16 @@ web3-bzz@1.2.11: swarm-js "^0.1.40" underscore "1.9.1" +web3-bzz@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.2.tgz#a3b9f613c49fd3e120e0997088a73557d5adb724" + integrity sha512-b1O2ObsqUN1lJxmFSjvnEC4TsaCbmh7Owj3IAIWTKqL9qhVgx7Qsu5O9cD13pBiSPNZJ68uJPaKq380QB4NWeA== + dependencies: + "@types/node" "^10.12.18" + got "9.6.0" + swarm-js "0.1.39" + underscore "1.9.1" + web3-core-helpers@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" @@ -9515,6 +10053,15 @@ web3-core-helpers@1.2.11: web3-eth-iban "1.2.11" web3-utils "1.2.11" +web3-core-helpers@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.2.tgz#484974f4bd4a487217b85b0d7cfe841af0907619" + integrity sha512-HJrRsIGgZa1jGUIhvGz4S5Yh6wtOIo/TMIsSLe+Xay+KVnbseJpPprDI5W3s7H2ODhMQTbogmmUFquZweW2ImQ== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.2" + web3-utils "1.2.2" + web3-core-method@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" @@ -9527,6 +10074,17 @@ web3-core-method@1.2.11: web3-core-subscriptions "1.2.11" web3-utils "1.2.11" +web3-core-method@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.2.tgz#d4fe2bb1945b7152e5f08e4ea568b171132a1e56" + integrity sha512-szR4fDSBxNHaF1DFqE+j6sFR/afv9Aa36OW93saHZnrh+iXSrYeUUDfugeNcRlugEKeUCkd4CZylfgbK2SKYJA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-core-promievent "1.2.2" + web3-core-subscriptions "1.2.2" + web3-utils "1.2.2" + web3-core-promievent@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" @@ -9534,6 +10092,14 @@ web3-core-promievent@1.2.11: dependencies: eventemitter3 "4.0.4" +web3-core-promievent@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.2.tgz#3b60e3f2a0c96db8a891c927899d29d39e66ab1c" + integrity sha512-tKvYeT8bkUfKABcQswK6/X79blKTKYGk949urZKcLvLDEaWrM3uuzDwdQT3BNKzQ3vIvTggFPX9BwYh0F1WwqQ== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + web3-core-requestmanager@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" @@ -9545,6 +10111,17 @@ web3-core-requestmanager@1.2.11: web3-providers-ipc "1.2.11" web3-providers-ws "1.2.11" +web3-core-requestmanager@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.2.tgz#667ba9ac724c9c76fa8965ae8a3c61f66e68d8d6" + integrity sha512-a+gSbiBRHtHvkp78U2bsntMGYGF2eCb6219aMufuZWeAZGXJ63Wc2321PCbA8hF9cQrZI4EoZ4kVLRI4OF15Hw== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-providers-http "1.2.2" + web3-providers-ipc "1.2.2" + web3-providers-ws "1.2.2" + web3-core-subscriptions@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" @@ -9554,6 +10131,15 @@ web3-core-subscriptions@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" +web3-core-subscriptions@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.2.tgz#bf4ba23a653a003bdc3551649958cc0b080b068e" + integrity sha512-QbTgigNuT4eicAWWr7ahVpJyM8GbICsR1Ys9mJqzBEwpqS+RXTRVSkwZ2IsxO+iqv6liMNwGregbJLq4urMFcQ== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-core@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" @@ -9567,6 +10153,18 @@ web3-core@1.2.11: web3-core-requestmanager "1.2.11" web3-utils "1.2.11" +web3-core@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.2.tgz#334b99c8222ef9cfd0339e27352f0b58ea789a2f" + integrity sha512-miHAX3qUgxV+KYfaOY93Hlc3kLW2j5fH8FJy6kSxAv+d4d5aH0wwrU2IIoJylQdT+FeenQ38sgsCnFu9iZ1hCQ== + dependencies: + "@types/bn.js" "^4.11.4" + "@types/node" "^12.6.1" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-requestmanager "1.2.2" + web3-utils "1.2.2" + web3-eth-abi@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" @@ -9576,6 +10174,15 @@ web3-eth-abi@1.2.11: underscore "1.9.1" web3-utils "1.2.11" +web3-eth-abi@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.2.tgz#d5616d88a90020f894763423a9769f2da11fe37a" + integrity sha512-Yn/ZMgoOLxhTVxIYtPJ0eS6pnAnkTAaJgUJh1JhZS4ekzgswMfEYXOwpMaD5eiqPJLpuxmZFnXnBZlnQ1JMXsw== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.2" + web3-eth-accounts@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" @@ -9593,6 +10200,24 @@ web3-eth-accounts@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" +web3-eth-accounts@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.2.tgz#c187e14bff6baa698ac352220290222dbfd332e5" + integrity sha512-KzHOEyXOEZ13ZOkWN3skZKqSo5f4Z1ogPFNn9uZbKCz+kSp+gCAEKxyfbOsB/JMAp5h7o7pb6eYsPCUBJmFFiA== + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-shim "github:web3-js/scrypt-shim" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-utils "1.2.2" + web3-eth-contract@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" @@ -9608,6 +10233,21 @@ web3-eth-contract@1.2.11: web3-eth-abi "1.2.11" web3-utils "1.2.11" +web3-eth-contract@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.2.tgz#84e92714918a29e1028ee7718f0712536e14e9a1" + integrity sha512-EKT2yVFws3FEdotDQoNsXTYL798+ogJqR2//CaGwx3p0/RvQIgfzEwp8nbgA6dMxCsn9KOQi7OtklzpnJMkjtA== + dependencies: + "@types/bn.js" "^4.11.4" + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-promievent "1.2.2" + web3-core-subscriptions "1.2.2" + web3-eth-abi "1.2.2" + web3-utils "1.2.2" + web3-eth-ens@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" @@ -9623,6 +10263,20 @@ web3-eth-ens@1.2.11: web3-eth-contract "1.2.11" web3-utils "1.2.11" +web3-eth-ens@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.2.tgz#0a4abed1d4cbdacbf5e1ab06e502d806d1192bc6" + integrity sha512-CFjkr2HnuyMoMFBoNUWojyguD4Ef+NkyovcnUc/iAb9GP4LHohKrODG4pl76R5u61TkJGobC2ij6TyibtsyVYg== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-promievent "1.2.2" + web3-eth-abi "1.2.2" + web3-eth-contract "1.2.2" + web3-utils "1.2.2" + web3-eth-iban@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" @@ -9631,6 +10285,14 @@ web3-eth-iban@1.2.11: bn.js "^4.11.9" web3-utils "1.2.11" +web3-eth-iban@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.2.tgz#76bec73bad214df7c4192388979a59fc98b96c5a" + integrity sha512-gxKXBoUhaTFHr0vJB/5sd4i8ejF/7gIsbM/VvemHT3tF5smnmY6hcwSMmn7sl5Gs+83XVb/BngnnGkf+I/rsrQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.2" + web3-eth-personal@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" @@ -9643,6 +10305,18 @@ web3-eth-personal@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" +web3-eth-personal@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.2.tgz#eee1c86a8132fa16b5e34c6d421ca92e684f0be6" + integrity sha512-4w+GLvTlFqW3+q4xDUXvCEMU7kRZ+xm/iJC8gm1Li1nXxwwFbs+Y+KBK6ZYtoN1qqAnHR+plYpIoVo27ixI5Rg== + dependencies: + "@types/node" "^12.6.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-net "1.2.2" + web3-utils "1.2.2" + web3-eth@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" @@ -9662,6 +10336,25 @@ web3-eth@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" +web3-eth@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.2.tgz#65a1564634a23b990efd1655bf94ad513904286c" + integrity sha512-UXpC74mBQvZzd4b+baD4Ocp7g+BlwxhBHumy9seyE/LMIcMlePXwCKzxve9yReNpjaU16Mmyya6ZYlyiKKV8UA== + dependencies: + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-subscriptions "1.2.2" + web3-eth-abi "1.2.2" + web3-eth-accounts "1.2.2" + web3-eth-contract "1.2.2" + web3-eth-ens "1.2.2" + web3-eth-iban "1.2.2" + web3-eth-personal "1.2.2" + web3-net "1.2.2" + web3-utils "1.2.2" + web3-net@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" @@ -9671,6 +10364,15 @@ web3-net@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" +web3-net@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.2.tgz#5c3226ca72df7c591422440ce6f1203fd42ddad9" + integrity sha512-K07j2DXq0x4UOJgae65rWZKraOznhk8v5EGSTdFqASTx7vWE/m+NqBijBYGEsQY1lSMlVaAY9UEQlcXK5HzXTw== + dependencies: + web3-core "1.2.2" + web3-core-method "1.2.2" + web3-utils "1.2.2" + web3-provider-engine@14.2.1: version "14.2.1" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz#ef351578797bf170e08d529cb5b02f8751329b95" @@ -9705,6 +10407,14 @@ web3-providers-http@1.2.11: web3-core-helpers "1.2.11" xhr2-cookies "1.1.0" +web3-providers-http@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.2.tgz#155e55c1d69f4c5cc0b411ede40dea3d06720956" + integrity sha512-BNZ7Hguy3eBszsarH5gqr9SIZNvqk9eKwqwmGH1LQS1FL3NdoOn7tgPPdddrXec4fL94CwgNk4rCU+OjjZRNDg== + dependencies: + web3-core-helpers "1.2.2" + xhr2-cookies "1.1.0" + web3-providers-ipc@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" @@ -9714,6 +10424,15 @@ web3-providers-ipc@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" +web3-providers-ipc@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.2.tgz#c6d165a12bc68674b4cdd543ea18aec79cafc2e8" + integrity sha512-t97w3zi5Kn/LEWGA6D9qxoO0LBOG+lK2FjlEdCwDQatffB/+vYrzZ/CLYVQSoyFZAlsDoBasVoYSWZK1n39aHA== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-providers-ws@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" @@ -9724,6 +10443,15 @@ web3-providers-ws@1.2.11: web3-core-helpers "1.2.11" websocket "^1.0.31" +web3-providers-ws@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.2.tgz#d2c05c68598cea5ad3fa6ef076c3bcb3ca300d29" + integrity sha512-Wb1mrWTGMTXOpJkL0yGvL/WYLt8fUIXx8k/l52QB2IiKzvyd42dTWn4+j8IKXGSYYzOm7NMqv6nhA5VDk12VfA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + websocket "github:web3-js/WebSocket-Node#polyfill/globalThis" + web3-shh@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" @@ -9734,6 +10462,16 @@ web3-shh@1.2.11: web3-core-subscriptions "1.2.11" web3-net "1.2.11" +web3-shh@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.2.tgz#44ed998f2a6ba0ec5cb9d455184a0f647826a49c" + integrity sha512-og258NPhlBn8yYrDWjoWBBb6zo1OlBgoWGT+LL5/LPqRbjPe09hlOYHgscAAr9zZGtohTOty7RrxYw6Z6oDWCg== + dependencies: + web3-core "1.2.2" + web3-core-method "1.2.2" + web3-core-subscriptions "1.2.2" + web3-net "1.2.2" + web3-utils@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" @@ -9748,6 +10486,20 @@ web3-utils@1.2.11: underscore "1.9.1" utf8 "3.0.0" +web3-utils@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.2.tgz#b53a08c40d2c3f31d3c4a28e7d749405df99c8c0" + integrity sha512-joF+s3243TY5cL7Z7y4h1JsJpUCf/kmFmj+eJar7Y2yNIGVcW961VyrAms75tjUysSuHaUQ3eQXjBEUJueT52A== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + web3-utils@^1.0.0-beta.31: version "1.6.0" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.6.0.tgz#1975c5ee5b7db8a0836eb7004848a7cd962d1ddc" @@ -9774,6 +10526,20 @@ web3@1.2.11: web3-shh "1.2.11" web3-utils "1.2.11" +web3@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.2.tgz#b1b8b69aafdf94cbaeadbb68a8aa1df2ef266aec" + integrity sha512-/ChbmB6qZpfGx6eNpczt5YSUBHEA5V2+iUCbn85EVb3Zv6FVxrOo5Tv7Lw0gE2tW7EEjASbCyp3mZeiZaCCngg== + dependencies: + "@types/node" "^12.6.1" + web3-bzz "1.2.2" + web3-core "1.2.2" + web3-eth "1.2.2" + web3-eth-personal "1.2.2" + web3-net "1.2.2" + web3-shh "1.2.2" + web3-utils "1.2.2" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -9803,6 +10569,16 @@ websocket@^1.0.31: utf-8-validate "^5.0.2" yaeti "^0.0.6" +"websocket@github:web3-js/WebSocket-Node#polyfill/globalThis": + version "1.0.29" + resolved "https://codeload.github.com/web3-js/WebSocket-Node/tar.gz/ef5ea2f41daf4a2113b80c9223df884b4d56c400" + dependencies: + debug "^2.2.0" + es5-ext "^0.10.50" + nan "^2.14.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + whatwg-fetch@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" @@ -10071,6 +10847,14 @@ yargs@^4.7.1: y18n "^3.2.1" yargs-parser "^2.4.1" +yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"