From 072ae63a04c6cc7d1778a7c35fccfe5bcf27e026 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 17 Jun 2020 17:06:39 +0200 Subject: [PATCH 1/9] Extracted common bonding parts from KeepBonding Moved parts of the code that are common and will be used for ETH bonding from KeepBonding to AbstractBonding contract. Only withdraw related functions remain in KeepBonding as they require different roles handling. --- solidity/contracts/AbstractBonding.sol | 348 +++++++++++++++++++++++++ solidity/contracts/KeepBonding.sol | 323 +---------------------- 2 files changed, 353 insertions(+), 318 deletions(-) create mode 100644 solidity/contracts/AbstractBonding.sol diff --git a/solidity/contracts/AbstractBonding.sol b/solidity/contracts/AbstractBonding.sol new file mode 100644 index 000000000..897c899a4 --- /dev/null +++ b/solidity/contracts/AbstractBonding.sol @@ -0,0 +1,348 @@ +/** +▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄ +▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ + ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓ + ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀ + ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ +▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ + + Trust math, not hardware. +*/ + +pragma solidity 0.5.17; + +import "@keep-network/keep-core/contracts/KeepRegistry.sol"; +import "@keep-network/keep-core/contracts/KeepStaking.sol"; +import "@keep-network/sortition-pools/contracts/api/IBonding.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/// @title Keep Bonding +/// @notice Contract holding deposits from keeps' operators. +contract AbstractBonding is IBonding { + using SafeMath for uint256; + + // Registry contract with a list of approved factories (operator contracts). + KeepRegistry internal registry; + + // Staking contract. + KeepStaking internal staking; + + // Unassigned value in wei deposited by operators. + mapping(address => uint256) public unbondedValue; + + // References to created bonds. Bond identifier is built from operator's + // address, holder's address and reference ID assigned on bond creation. + mapping(bytes32 => uint256) internal lockedBonds; + + // Sortition pools authorized by operator's authorizer. + // operator -> pool -> boolean + mapping(address => mapping(address => bool)) internal authorizedPools; + + event UnbondedValueDeposited( + address indexed operator, + address indexed beneficiary, + uint256 amount + ); + event UnbondedValueWithdrawn( + address indexed operator, + address indexed beneficiary, + uint256 amount + ); + event BondCreated( + address indexed operator, + address indexed holder, + address indexed sortitionPool, + uint256 referenceID, + uint256 amount + ); + event BondReassigned( + address indexed operator, + uint256 indexed referenceID, + address newHolder, + uint256 newReferenceID + ); + event BondReleased(address indexed operator, uint256 indexed referenceID); + event BondSeized( + address indexed operator, + uint256 indexed referenceID, + address destination, + uint256 amount + ); + + /// @notice Initializes Keep Bonding contract. + /// @param registryAddress Keep registry contract address. + /// @param stakingContractAddress Keep staking contract address. + constructor(address registryAddress, address stakingContractAddress) + public + { + registry = KeepRegistry(registryAddress); + staking = KeepStaking(stakingContractAddress); // Rename to: Staking + } + + /// @notice Add the provided value to operator's pool available for bonding. + /// @param operator Address of the operator. + function deposit(address operator) external payable { + address beneficiary = tokenStaking.beneficiaryOf(operator); + // Beneficiary has to be set (delegation exist) before an operator can + // deposit wei. It protects from a situation when an operator wants + // to withdraw funds which are transfered to beneficiary with zero + // address. + require( + beneficiary != address(0), + "Beneficiary not defined for the operator" + ); + unbondedValue[operator] = unbondedValue[operator].add(msg.value); + emit UnbondedValueDeposited(operator, beneficiary, msg.value); + } + + /// @notice Withdraws amount from operator's value available for bonding. + /// @param amount Value to withdraw in wei. + /// @param operator Address of the operator. + function withdraw(uint256 amount, address operator) public; + + /// @notice Returns the amount of wei the operator has made available for + /// bonding and that is still unbounded. If the operator doesn't exist or + /// bond creator is not authorized as an operator contract or it is not + /// authorized by the operator or there is no secondary authorization for + /// the provided sortition pool, function returns 0. + /// @dev Implements function expected by sortition pools' IBonding interface. + /// @param operator Address of the operator. + /// @param bondCreator Address authorized to create a bond. + /// @param authorizedSortitionPool Address of authorized sortition pool. + /// @return Amount of authorized wei deposit available for bonding. + function availableUnbondedValue( + address operator, + address bondCreator, + address authorizedSortitionPool + ) public view returns (uint256) { + // Sortition pools check this condition and skips operators that + // are no longer eligible. We cannot revert here. + if ( + registry.isApprovedOperatorContract(bondCreator) && + staking.isAuthorizedForOperator(operator, bondCreator) && + hasSecondaryAuthorization(operator, authorizedSortitionPool) + ) { + return unbondedValue[operator]; + } + + return 0; + } + + /// @notice Create bond for the given operator, holder, reference and amount. + /// @dev Function can be executed only by authorized contract. Reference ID + /// should be unique for holder and operator. + /// @param operator Address of the operator to bond. + /// @param holder Address of the holder of the bond. + /// @param referenceID Reference ID used to track the bond by holder. + /// @param amount Value to bond in wei. + /// @param authorizedSortitionPool Address of authorized sortition pool. + function createBond( + address operator, + address holder, + uint256 referenceID, + uint256 amount, + address authorizedSortitionPool + ) public { + require( + availableUnbondedValue( + operator, + msg.sender, + authorizedSortitionPool + ) >= amount, + "Insufficient unbonded value" + ); + + bytes32 bondID = keccak256( + abi.encodePacked(operator, holder, referenceID) + ); + + require( + lockedBonds[bondID] == 0, + "Reference ID not unique for holder and operator" + ); + + unbondedValue[operator] = unbondedValue[operator].sub(amount); + lockedBonds[bondID] = lockedBonds[bondID].add(amount); + + emit BondCreated( + operator, + holder, + authorizedSortitionPool, + referenceID, + amount + ); + } + + /// @notice Returns value of wei bonded for the operator. + /// @param operator Address of the operator. + /// @param holder Address of the holder of the bond. + /// @param referenceID Reference ID of the bond. + /// @return Amount of wei in the selected bond. + function bondAmount( + address operator, + address holder, + uint256 referenceID + ) public view returns (uint256) { + bytes32 bondID = keccak256( + abi.encodePacked(operator, holder, referenceID) + ); + + return lockedBonds[bondID]; + } + + /// @notice Reassigns a bond to a new holder under a new reference. + /// @dev Function requires that a caller is the current holder of the bond + /// which is being reassigned. + /// @param operator Address of the bonded operator. + /// @param referenceID Reference ID of the bond. + /// @param newHolder Address of the new holder of the bond. + /// @param newReferenceID New reference ID to register the bond. + function reassignBond( + address operator, + uint256 referenceID, + address newHolder, + uint256 newReferenceID + ) public { + address holder = msg.sender; + bytes32 bondID = keccak256( + abi.encodePacked(operator, holder, referenceID) + ); + + require(lockedBonds[bondID] > 0, "Bond not found"); + + bytes32 newBondID = keccak256( + abi.encodePacked(operator, newHolder, newReferenceID) + ); + + require( + lockedBonds[newBondID] == 0, + "Reference ID not unique for holder and operator" + ); + + lockedBonds[newBondID] = lockedBonds[bondID]; + lockedBonds[bondID] = 0; + + emit BondReassigned(operator, referenceID, newHolder, newReferenceID); + } + + /// @notice Releases the bond and moves the bond value to the operator's + /// unbounded value pool. + /// @dev Function requires that caller is the holder of the bond which is + /// being released. + /// @param operator Address of the bonded operator. + /// @param referenceID Reference ID of the bond. + function freeBond(address operator, uint256 referenceID) public { + address holder = msg.sender; + bytes32 bondID = keccak256( + abi.encodePacked(operator, holder, referenceID) + ); + + require(lockedBonds[bondID] > 0, "Bond not found"); + + uint256 amount = lockedBonds[bondID]; + lockedBonds[bondID] = 0; + unbondedValue[operator] = unbondedValue[operator].add(amount); + + emit BondReleased(operator, referenceID); + } + + /// @notice Seizes the bond by moving some or all of the locked bond to the + /// provided destination address. + /// @dev Function requires that a caller is the holder of the bond which is + /// being seized. + /// @param operator Address of the bonded operator. + /// @param referenceID Reference ID of the bond. + /// @param amount Amount to be seized. + /// @param destination Address to send the amount to. + function seizeBond( + address operator, + uint256 referenceID, + uint256 amount, + address payable destination + ) public { + require(amount > 0, "Requested amount should be greater than zero"); + + address payable holder = msg.sender; + bytes32 bondID = keccak256( + abi.encodePacked(operator, holder, referenceID) + ); + + require( + lockedBonds[bondID] >= amount, + "Requested amount is greater than the bond" + ); + + lockedBonds[bondID] = lockedBonds[bondID].sub(amount); + + (bool success, ) = destination.call.value(amount)(""); + require(success, "Transfer failed"); + + emit BondSeized(operator, referenceID, destination, amount); + } + + /// @notice Authorizes sortition pool for the provided operator. + /// Operator's authorizers need to authorize individual sortition pools + /// per application since they may be interested in participating only in + /// a subset of keep types used by the given application. + /// @dev Only operator's authorizer can call this function. + function authorizeSortitionPoolContract( + address _operator, + address _poolAddress + ) public { + require( + staking.authorizerOf(_operator) == msg.sender, + "Not authorized" + ); + authorizedPools[_operator][_poolAddress] = true; + } + + /// @notice Deauthorizes sortition pool for the provided operator. + /// Authorizer may deauthorize individual sortition pool in case the + /// operator should no longer be eligible for work selection and the + /// application represented by the sortition pool should no longer be + /// eligible to create bonds for the operator. + /// @dev Only operator's authorizer can call this function. + function deauthorizeSortitionPoolContract( + address _operator, + address _poolAddress + ) public { + require( + staking.authorizerOf(_operator) == msg.sender, + "Not authorized" + ); + authorizedPools[_operator][_poolAddress] = false; + } + + /// @notice Checks if the sortition pool has been authorized for the + /// provided operator by its authorizer. + /// @dev See authorizeSortitionPoolContract. + function hasSecondaryAuthorization(address _operator, address _poolAddress) + public + view + returns (bool) + { + return authorizedPools[_operator][_poolAddress]; + } + + /// @notice Withdraws the provided amount from unbonded value of the + /// provided operator to operator's beneficiary. If there is no enough + /// unbonded value or the transfer failed, function fails. + function withdrawBond(uint256 amount, address operator) internal { + require( + unbondedValue[operator] >= amount, + "Insufficient unbonded value" + ); + + unbondedValue[operator] = unbondedValue[operator].sub(amount); + + address beneficiary = staking.beneficiaryOf(operator); + + (bool success, ) = beneficiary.call.value(amount)(""); + require(success, "Transfer failed"); + + emit UnbondedValueWithdrawn(operator, beneficiary, amount); + } +} diff --git a/solidity/contracts/KeepBonding.sol b/solidity/contracts/KeepBonding.sol index 6e45ae1ef..3fa57b578 100644 --- a/solidity/contracts/KeepBonding.sol +++ b/solidity/contracts/KeepBonding.sol @@ -14,71 +14,19 @@ pragma solidity 0.5.17; -import "@keep-network/keep-core/contracts/KeepRegistry.sol"; -import "@keep-network/keep-core/contracts/TokenStaking.sol"; +import "./AbstractBonding.sol"; + import "@keep-network/keep-core/contracts/TokenGrant.sol"; import "@keep-network/keep-core/contracts/libraries/RolesLookup.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - - /// @title Keep Bonding /// @notice Contract holding deposits from keeps' operators. -contract KeepBonding { - using SafeMath for uint256; +contract KeepBonding is AbstractBonding { using RolesLookup for address payable; - // Registry contract with a list of approved factories (operator contracts). - KeepRegistry internal registry; - - // KEEP token staking contract. - TokenStaking internal tokenStaking; - // KEEP token grant contract. TokenGrant internal tokenGrant; - // Unassigned value in wei deposited by operators. - mapping(address => uint256) public unbondedValue; - - // References to created bonds. Bond identifier is built from operator's - // address, holder's address and reference ID assigned on bond creation. - mapping(bytes32 => uint256) internal lockedBonds; - - // Sortition pools authorized by operator's authorizer. - // operator -> pool -> boolean - mapping(address => mapping(address => bool)) internal authorizedPools; - - event UnbondedValueDeposited( - address indexed operator, - address indexed beneficiary, - uint256 amount - ); - event UnbondedValueWithdrawn( - address indexed operator, - address indexed beneficiary, - uint256 amount - ); - event BondCreated( - address indexed operator, - address indexed holder, - address indexed sortitionPool, - uint256 referenceID, - uint256 amount - ); - event BondReassigned( - address indexed operator, - uint256 indexed referenceID, - address newHolder, - uint256 newReferenceID - ); - event BondReleased(address indexed operator, uint256 indexed referenceID); - event BondSeized( - address indexed operator, - uint256 indexed referenceID, - address destination, - uint256 amount - ); - /// @notice Initializes Keep Bonding contract. /// @param registryAddress Keep registry contract address. /// @param tokenStakingAddress KEEP token staking contract address. @@ -87,56 +35,10 @@ contract KeepBonding { address registryAddress, address tokenStakingAddress, address tokenGrantAddress - ) public { - registry = KeepRegistry(registryAddress); - tokenStaking = TokenStaking(tokenStakingAddress); + ) public AbstractBonding(registryAddress, tokenStakingAddress) { tokenGrant = TokenGrant(tokenGrantAddress); } - /// @notice Add the provided value to operator's pool available for bonding. - /// @param operator Address of the operator. - function deposit(address operator) external payable { - address beneficiary = tokenStaking.beneficiaryOf(operator); - // Beneficiary has to be set (delegation exist) before an operator can - // deposit wei. It protects from a situation when an operator wants - // to withdraw funds which are transfered to beneficiary with zero - // address. - require( - beneficiary != address(0), - "Beneficiary not defined for the operator" - ); - unbondedValue[operator] = unbondedValue[operator].add(msg.value); - emit UnbondedValueDeposited(operator, beneficiary, msg.value); - } - - /// @notice Returns the amount of wei the operator has made available for - /// bonding and that is still unbounded. If the operator doesn't exist or - /// bond creator is not authorized as an operator contract or it is not - /// authorized by the operator or there is no secondary authorization for - /// the provided sortition pool, function returns 0. - /// @dev Implements function expected by sortition pools' IBonding interface. - /// @param operator Address of the operator. - /// @param bondCreator Address authorized to create a bond. - /// @param authorizedSortitionPool Address of authorized sortition pool. - /// @return Amount of authorized wei deposit available for bonding. - function availableUnbondedValue( - address operator, - address bondCreator, - address authorizedSortitionPool - ) public view returns (uint256) { - // Sortition pools check this condition and skips operators that - // are no longer eligible. We cannot revert here. - if ( - registry.isApprovedOperatorContract(bondCreator) && - tokenStaking.isAuthorizedForOperator(operator, bondCreator) && - hasSecondaryAuthorization(operator, authorizedSortitionPool) - ) { - return unbondedValue[operator]; - } - - return 0; - } - /// @notice Withdraws amount from operator's value available for bonding. /// Should not be used by grantee of managed grants. For this case, /// please use `withdrawAsManagedGrantee`. @@ -151,7 +53,7 @@ contract KeepBonding { function withdraw(uint256 amount, address operator) public { require( msg.sender == operator || - msg.sender.isTokenOwnerForOperator(operator, tokenStaking) || + msg.sender.isTokenOwnerForOperator(operator, staking) || msg.sender.isGranteeForOperator(operator, tokenGrant), "Only operator or the owner is allowed to withdraw bond" ); @@ -180,219 +82,4 @@ contract KeepBonding { withdrawBond(amount, operator); } - - /// @notice Create bond for the given operator, holder, reference and amount. - /// @dev Function can be executed only by authorized contract. Reference ID - /// should be unique for holder and operator. - /// @param operator Address of the operator to bond. - /// @param holder Address of the holder of the bond. - /// @param referenceID Reference ID used to track the bond by holder. - /// @param amount Value to bond in wei. - /// @param authorizedSortitionPool Address of authorized sortition pool. - function createBond( - address operator, - address holder, - uint256 referenceID, - uint256 amount, - address authorizedSortitionPool - ) public { - require( - availableUnbondedValue( - operator, - msg.sender, - authorizedSortitionPool - ) >= amount, - "Insufficient unbonded value" - ); - - bytes32 bondID = keccak256( - abi.encodePacked(operator, holder, referenceID) - ); - - require( - lockedBonds[bondID] == 0, - "Reference ID not unique for holder and operator" - ); - - unbondedValue[operator] = unbondedValue[operator].sub(amount); - lockedBonds[bondID] = lockedBonds[bondID].add(amount); - - emit BondCreated( - operator, - holder, - authorizedSortitionPool, - referenceID, - amount - ); - } - - /// @notice Returns value of wei bonded for the operator. - /// @param operator Address of the operator. - /// @param holder Address of the holder of the bond. - /// @param referenceID Reference ID of the bond. - /// @return Amount of wei in the selected bond. - function bondAmount(address operator, address holder, uint256 referenceID) - public - view - returns (uint256) - { - bytes32 bondID = keccak256( - abi.encodePacked(operator, holder, referenceID) - ); - - return lockedBonds[bondID]; - } - - /// @notice Reassigns a bond to a new holder under a new reference. - /// @dev Function requires that a caller is the current holder of the bond - /// which is being reassigned. - /// @param operator Address of the bonded operator. - /// @param referenceID Reference ID of the bond. - /// @param newHolder Address of the new holder of the bond. - /// @param newReferenceID New reference ID to register the bond. - function reassignBond( - address operator, - uint256 referenceID, - address newHolder, - uint256 newReferenceID - ) public { - address holder = msg.sender; - bytes32 bondID = keccak256( - abi.encodePacked(operator, holder, referenceID) - ); - - require(lockedBonds[bondID] > 0, "Bond not found"); - - bytes32 newBondID = keccak256( - abi.encodePacked(operator, newHolder, newReferenceID) - ); - - require( - lockedBonds[newBondID] == 0, - "Reference ID not unique for holder and operator" - ); - - lockedBonds[newBondID] = lockedBonds[bondID]; - lockedBonds[bondID] = 0; - - emit BondReassigned(operator, referenceID, newHolder, newReferenceID); - } - - /// @notice Releases the bond and moves the bond value to the operator's - /// unbounded value pool. - /// @dev Function requires that caller is the holder of the bond which is - /// being released. - /// @param operator Address of the bonded operator. - /// @param referenceID Reference ID of the bond. - function freeBond(address operator, uint256 referenceID) public { - address holder = msg.sender; - bytes32 bondID = keccak256( - abi.encodePacked(operator, holder, referenceID) - ); - - require(lockedBonds[bondID] > 0, "Bond not found"); - - uint256 amount = lockedBonds[bondID]; - lockedBonds[bondID] = 0; - unbondedValue[operator] = unbondedValue[operator].add(amount); - - emit BondReleased(operator, referenceID); - } - - /// @notice Seizes the bond by moving some or all of the locked bond to the - /// provided destination address. - /// @dev Function requires that a caller is the holder of the bond which is - /// being seized. - /// @param operator Address of the bonded operator. - /// @param referenceID Reference ID of the bond. - /// @param amount Amount to be seized. - /// @param destination Address to send the amount to. - function seizeBond( - address operator, - uint256 referenceID, - uint256 amount, - address payable destination - ) public { - require(amount > 0, "Requested amount should be greater than zero"); - - address payable holder = msg.sender; - bytes32 bondID = keccak256( - abi.encodePacked(operator, holder, referenceID) - ); - - require( - lockedBonds[bondID] >= amount, - "Requested amount is greater than the bond" - ); - - lockedBonds[bondID] = lockedBonds[bondID].sub(amount); - - (bool success, ) = destination.call.value(amount)(""); - require(success, "Transfer failed"); - - emit BondSeized(operator, referenceID, destination, amount); - } - - /// @notice Authorizes sortition pool for the provided operator. - /// Operator's authorizers need to authorize individual sortition pools - /// per application since they may be interested in participating only in - /// a subset of keep types used by the given application. - /// @dev Only operator's authorizer can call this function. - function authorizeSortitionPoolContract( - address _operator, - address _poolAddress - ) public { - require( - tokenStaking.authorizerOf(_operator) == msg.sender, - "Not authorized" - ); - authorizedPools[_operator][_poolAddress] = true; - } - - /// @notice Deauthorizes sortition pool for the provided operator. - /// Authorizer may deauthorize individual sortition pool in case the - /// operator should no longer be eligible for work selection and the - /// application represented by the sortition pool should no longer be - /// eligible to create bonds for the operator. - /// @dev Only operator's authorizer can call this function. - function deauthorizeSortitionPoolContract( - address _operator, - address _poolAddress - ) public { - require( - tokenStaking.authorizerOf(_operator) == msg.sender, - "Not authorized" - ); - authorizedPools[_operator][_poolAddress] = false; - } - - /// @notice Checks if the sortition pool has been authorized for the - /// provided operator by its authorizer. - /// @dev See authorizeSortitionPoolContract. - function hasSecondaryAuthorization(address _operator, address _poolAddress) - public - view - returns (bool) - { - return authorizedPools[_operator][_poolAddress]; - } - - /// @notice Withdraws the provided amount from unbonded value of the - /// provided operator to operator's beneficiary. If there is no enough - /// unbonded value or the transfer failed, function fails. - function withdrawBond(uint256 amount, address operator) internal { - require( - unbondedValue[operator] >= amount, - "Insufficient unbonded value" - ); - - unbondedValue[operator] = unbondedValue[operator].sub(amount); - - address beneficiary = tokenStaking.beneficiaryOf(operator); - - (bool success, ) = beneficiary.call.value(amount)(""); - require(success, "Transfer failed"); - - emit UnbondedValueWithdrawn(operator, beneficiary, amount); - } } From da5ce3d4fb19afb3c531fb4b1cfca41148a9aefb Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Mon, 22 Jun 2020 15:58:25 +0200 Subject: [PATCH 2/9] Updated holding references to staking contract As TokenStaking contract has been refactored in keep-network/keep-core#1867 we can use Authorizations and StakeDelegatable contracts calls as these contracts define the functions we use in bonding. In this commit we use separately Authorizations and StakeDelegatable instead of TokenStaking to be more flexible and able to use common code from AbstractBonding for both KEEP token and ETH bonding. --- solidity/contracts/AbstractBonding.sol | 45 +++++++++++++++----------- solidity/contracts/KeepBonding.sol | 15 +++++++-- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/solidity/contracts/AbstractBonding.sol b/solidity/contracts/AbstractBonding.sol index 897c899a4..3d22fe0df 100644 --- a/solidity/contracts/AbstractBonding.sol +++ b/solidity/contracts/AbstractBonding.sol @@ -15,10 +15,12 @@ pragma solidity 0.5.17; import "@keep-network/keep-core/contracts/KeepRegistry.sol"; -import "@keep-network/keep-core/contracts/KeepStaking.sol"; +import "@keep-network/keep-core/contracts/Authorizations.sol"; +import "@keep-network/keep-core/contracts/StakeDelegatable.sol"; import "@keep-network/sortition-pools/contracts/api/IBonding.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + /// @title Keep Bonding /// @notice Contract holding deposits from keeps' operators. contract AbstractBonding is IBonding { @@ -27,8 +29,11 @@ contract AbstractBonding is IBonding { // Registry contract with a list of approved factories (operator contracts). KeepRegistry internal registry; - // Staking contract. - KeepStaking internal staking; + // Staking Authorizations contract. + Authorizations internal authorizations; + + // Stake Delegatable contract. + StakeDelegatable internal stakeDelegatable; // Unassigned value in wei deposited by operators. mapping(address => uint256) public unbondedValue; @@ -74,18 +79,22 @@ contract AbstractBonding is IBonding { /// @notice Initializes Keep Bonding contract. /// @param registryAddress Keep registry contract address. - /// @param stakingContractAddress Keep staking contract address. - constructor(address registryAddress, address stakingContractAddress) - public - { + /// @param authorizationsAddress Staking Authorizations contract address. + /// @param stakeDelegatableAddress Stake Delegatable contract address. + constructor( + address registryAddress, + address authorizationsAddress, + address stakeDelegatableAddress + ) public { registry = KeepRegistry(registryAddress); - staking = KeepStaking(stakingContractAddress); // Rename to: Staking + authorizations = Authorizations(authorizationsAddress); + stakeDelegatable = StakeDelegatable(stakeDelegatableAddress); } /// @notice Add the provided value to operator's pool available for bonding. /// @param operator Address of the operator. function deposit(address operator) external payable { - address beneficiary = tokenStaking.beneficiaryOf(operator); + address beneficiary = stakeDelegatable.beneficiaryOf(operator); // Beneficiary has to be set (delegation exist) before an operator can // deposit wei. It protects from a situation when an operator wants // to withdraw funds which are transfered to beneficiary with zero @@ -122,7 +131,7 @@ contract AbstractBonding is IBonding { // are no longer eligible. We cannot revert here. if ( registry.isApprovedOperatorContract(bondCreator) && - staking.isAuthorizedForOperator(operator, bondCreator) && + authorizations.isAuthorizedForOperator(operator, bondCreator) && hasSecondaryAuthorization(operator, authorizedSortitionPool) ) { return unbondedValue[operator]; @@ -181,11 +190,11 @@ contract AbstractBonding is IBonding { /// @param holder Address of the holder of the bond. /// @param referenceID Reference ID of the bond. /// @return Amount of wei in the selected bond. - function bondAmount( - address operator, - address holder, - uint256 referenceID - ) public view returns (uint256) { + function bondAmount(address operator, address holder, uint256 referenceID) + public + view + returns (uint256) + { bytes32 bondID = keccak256( abi.encodePacked(operator, holder, referenceID) ); @@ -293,7 +302,7 @@ contract AbstractBonding is IBonding { address _poolAddress ) public { require( - staking.authorizerOf(_operator) == msg.sender, + stakeDelegatable.authorizerOf(_operator) == msg.sender, "Not authorized" ); authorizedPools[_operator][_poolAddress] = true; @@ -310,7 +319,7 @@ contract AbstractBonding is IBonding { address _poolAddress ) public { require( - staking.authorizerOf(_operator) == msg.sender, + stakeDelegatable.authorizerOf(_operator) == msg.sender, "Not authorized" ); authorizedPools[_operator][_poolAddress] = false; @@ -338,7 +347,7 @@ contract AbstractBonding is IBonding { unbondedValue[operator] = unbondedValue[operator].sub(amount); - address beneficiary = staking.beneficiaryOf(operator); + address beneficiary = stakeDelegatable.beneficiaryOf(operator); (bool success, ) = beneficiary.call.value(amount)(""); require(success, "Transfer failed"); diff --git a/solidity/contracts/KeepBonding.sol b/solidity/contracts/KeepBonding.sol index 3fa57b578..a0b2dddec 100644 --- a/solidity/contracts/KeepBonding.sol +++ b/solidity/contracts/KeepBonding.sol @@ -19,6 +19,7 @@ import "./AbstractBonding.sol"; import "@keep-network/keep-core/contracts/TokenGrant.sol"; import "@keep-network/keep-core/contracts/libraries/RolesLookup.sol"; + /// @title Keep Bonding /// @notice Contract holding deposits from keeps' operators. contract KeepBonding is AbstractBonding { @@ -35,7 +36,14 @@ contract KeepBonding is AbstractBonding { address registryAddress, address tokenStakingAddress, address tokenGrantAddress - ) public AbstractBonding(registryAddress, tokenStakingAddress) { + ) + public + AbstractBonding( + registryAddress, + tokenStakingAddress, // Authorizations + tokenStakingAddress // StakeDelegatable + ) + { tokenGrant = TokenGrant(tokenGrantAddress); } @@ -53,7 +61,10 @@ contract KeepBonding is AbstractBonding { function withdraw(uint256 amount, address operator) public { require( msg.sender == operator || - msg.sender.isTokenOwnerForOperator(operator, staking) || + msg.sender.isTokenOwnerForOperator( + operator, + stakeDelegatable + ) || msg.sender.isGranteeForOperator(operator, tokenGrant), "Only operator or the owner is allowed to withdraw bond" ); From 6655acacc1941dbd2a1f98e45f9d4531a9352a15 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Fri, 7 Aug 2020 16:39:46 +0200 Subject: [PATCH 3/9] Extracted common tests for bonding abstract contract --- .../contracts/test/AbstractBondingStub.sol | 26 + .../contracts/test/AuthorizationsStub.sol | 38 + .../contracts/test/StakeDelegetableStub.sol | 51 + solidity/test/AbstractBondingTest.js | 885 ++++++++++++++++++ solidity/test/KeepBondingTest.js | 652 +------------ 5 files changed, 1002 insertions(+), 650 deletions(-) create mode 100644 solidity/contracts/test/AbstractBondingStub.sol create mode 100644 solidity/contracts/test/AuthorizationsStub.sol create mode 100644 solidity/contracts/test/StakeDelegetableStub.sol create mode 100644 solidity/test/AbstractBondingTest.js diff --git a/solidity/contracts/test/AbstractBondingStub.sol b/solidity/contracts/test/AbstractBondingStub.sol new file mode 100644 index 000000000..7a6faed7b --- /dev/null +++ b/solidity/contracts/test/AbstractBondingStub.sol @@ -0,0 +1,26 @@ +pragma solidity 0.5.17; + +import "../../contracts/AbstractBonding.sol"; + +contract AbstractBondingStub is AbstractBonding { + constructor( + address registryAddress, + address authorizationsAddress, + address stakeDelegatableAddress + ) + public + AbstractBonding( + registryAddress, + authorizationsAddress, + stakeDelegatableAddress + ) + {} + + function withdraw(uint256 amount, address operator) public { + revert("abstract function"); + } + + function withdrawBondExposed(uint256 amount, address operator) public { + withdrawBond(amount, operator); + } +} diff --git a/solidity/contracts/test/AuthorizationsStub.sol b/solidity/contracts/test/AuthorizationsStub.sol new file mode 100644 index 000000000..f666250c6 --- /dev/null +++ b/solidity/contracts/test/AuthorizationsStub.sol @@ -0,0 +1,38 @@ +pragma solidity 0.5.17; + +import "@keep-network/keep-core/contracts/Authorizations.sol"; +import "@keep-network/keep-core/contracts/KeepRegistry.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/// @title Authorizations Stub +/// @dev This contract is for testing purposes only. +contract AuthorizationsStub is Authorizations { + // Authorized operator contracts. + mapping(address => mapping(address => bool)) internal authorizations; + + address public delegatedAuthority; + + constructor(KeepRegistry _registry) public Authorizations(_registry) {} + + function authorizeOperatorContract( + address _operator, + address _operatorContract + ) public { + authorizations[_operatorContract][_operator] = true; + } + + function isAuthorizedForOperator( + address _operator, + address _operatorContract + ) public view returns (bool) { + return authorizations[_operatorContract][_operator]; + } + + function authorizerOf(address _operator) public view returns (address) { + revert("abstract function"); + } + + function claimDelegatedAuthority(address delegatedAuthoritySource) public { + delegatedAuthority = delegatedAuthoritySource; + } +} diff --git a/solidity/contracts/test/StakeDelegetableStub.sol b/solidity/contracts/test/StakeDelegetableStub.sol new file mode 100644 index 000000000..f868c3159 --- /dev/null +++ b/solidity/contracts/test/StakeDelegetableStub.sol @@ -0,0 +1,51 @@ +pragma solidity 0.5.17; + +import "@keep-network/keep-core/contracts/StakeDelegatable.sol"; + +/// @title Stake Delegatable Stub +/// @dev This contract is for testing purposes only. +contract StakeDelegatableStub is StakeDelegatable { + mapping(address => uint256) stakes; + + mapping(address => address) operatorToOwner; + mapping(address => address payable) operatorToBeneficiary; + mapping(address => address) operatorToAuthorizer; + + function setBalance(address _operator, uint256 _balance) public { + stakes[_operator] = _balance; + } + + function balanceOf(address _address) public view returns (uint256 balance) { + return stakes[_address]; + } + + function setOwner(address _operator, address _owner) public { + operatorToOwner[_operator] = _owner; + } + + function ownerOf(address _operator) public view returns (address) { + return operatorToOwner[_operator]; + } + + function setBeneficiary(address _operator, address payable _beneficiary) + public + { + operatorToBeneficiary[_operator] = _beneficiary; + } + + function beneficiaryOf(address _operator) + public + view + returns (address payable) + { + return operatorToBeneficiary[_operator]; + } + + function setAuthorizer(address _operator, address _authorizer) public { + operatorToAuthorizer[_operator] = _authorizer; + } + + function authorizerOf(address _operator) public view returns (address) { + return operatorToAuthorizer[_operator]; + } +} diff --git a/solidity/test/AbstractBondingTest.js b/solidity/test/AbstractBondingTest.js new file mode 100644 index 000000000..a2624b4ce --- /dev/null +++ b/solidity/test/AbstractBondingTest.js @@ -0,0 +1,885 @@ +const {accounts, contract, web3} = require("@openzeppelin/test-environment") +const {createSnapshot, restoreSnapshot} = require("./helpers/snapshot") + +const KeepRegistry = contract.fromArtifact("KeepRegistry") +const AuthorizationsStub = contract.fromArtifact("AuthorizationsStub") +const StakeDelegatableStub = contract.fromArtifact("StakeDelegatableStub") +const AbstractBonding = contract.fromArtifact("AbstractBondingStub") +const TestEtherReceiver = contract.fromArtifact("TestEtherReceiver") + +const { + constants, + expectEvent, + expectRevert, +} = require("@openzeppelin/test-helpers") + +const BN = web3.utils.BN + +const chai = require("chai") +chai.use(require("bn-chai")(BN)) +const expect = chai.expect +const assert = chai.assert + +describe("AbstractBonding", function () { + let registry + let stakeDelegatable + let abstractBonding + let etherReceiver + + let operator + let authorizer + let bondCreator + let sortitionPool + let beneficiary + + before(async () => { + operator = accounts[1] + authorizer = accounts[2] + beneficiary = accounts[3] + bondCreator = accounts[4] + sortitionPool = accounts[5] + + registry = await KeepRegistry.new() + authorizations = await AuthorizationsStub.new(registry.address) + + stakeDelegatable = await StakeDelegatableStub.new() + + abstractBonding = await AbstractBonding.new( + registry.address, + authorizations.address, + stakeDelegatable.address + ) + + etherReceiver = await TestEtherReceiver.new() + + await registry.approveOperatorContract(bondCreator) + + await stakeDelegatable.setAuthorizer(operator, authorizer) + await abstractBonding.authorizeSortitionPoolContract( + operator, + sortitionPool, + { + from: authorizer, + } + ) + + await authorizations.authorizeOperatorContract(operator, bondCreator) + }) + + beforeEach(async () => { + await createSnapshot() + }) + + afterEach(async () => { + await restoreSnapshot() + }) + + describe("deposit", async () => { + const value = new BN(100) + const expectedUnbonded = value + + it("registers unbonded value", async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: value}) + const unbonded = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + + expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") + }) + + it("sums deposits", async () => { + const value1 = value + const value2 = new BN(230) + + await stakeDelegatable.setBeneficiary(operator, beneficiary) + + await abstractBonding.deposit(operator, {value: value1}) + expect( + await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + ).to.eq.BN(value1, "invalid unbonded value after first deposit") + + await abstractBonding.deposit(operator, {value: value2}) + expect( + await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + ).to.eq.BN( + value1.add(value2), + "invalid unbonded value after second deposit" + ) + }) + + it("emits event", async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + const receipt = await abstractBonding.deposit(operator, {value: value}) + expectEvent(receipt, "UnbondedValueDeposited", { + operator: operator, + beneficiary: beneficiary, + amount: value, + }) + }) + + it("reverts if beneficiary is not defined", async () => { + await stakeDelegatable.setBeneficiary(operator, constants.ZERO_ADDRESS) + + await expectRevert( + abstractBonding.deposit(operator, {value: value}), + "Beneficiary not defined for the operator" + ) + }) + }) + + describe("availableUnbondedValue", async () => { + const value = new BN(100) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: value}) + }) + + it("returns zero for operator with no deposit", async () => { + const unbondedOperator = "0x0000000000000000000000000000000000000001" + const expectedUnbonded = 0 + + const unbondedValue = await abstractBonding.availableUnbondedValue( + unbondedOperator, + bondCreator, + sortitionPool + ) + expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") + }) + + it("return zero when bond creator is not approved by operator", async () => { + const notApprovedBondCreator = + "0x0000000000000000000000000000000000000001" + const expectedUnbonded = 0 + + const unbondedValue = await abstractBonding.availableUnbondedValue( + operator, + notApprovedBondCreator, + sortitionPool + ) + expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") + }) + + it("returns zero when sortition pool is not authorized", async () => { + const notAuthorizedSortitionPool = + "0x0000000000000000000000000000000000000001" + const expectedUnbonded = 0 + + const unbondedValue = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + notAuthorizedSortitionPool + ) + expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") + }) + + it("returns value of operators deposit", async () => { + const expectedUnbonded = value + + const unbonded = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + + expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") + }) + }) + + describe("createBond", async () => { + const holder = accounts[3] + const value = new BN(100) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: value}) + }) + + it("creates bond", async () => { + const reference = new BN(888) + + const expectedUnbonded = 0 + + await abstractBonding.createBond( + operator, + holder, + reference, + value, + sortitionPool, + {from: bondCreator} + ) + + const unbonded = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") + + const lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(value, "unexpected bond value") + }) + + it("emits event", async () => { + const reference = new BN(999) + + const receipt = await abstractBonding.createBond( + operator, + holder, + reference, + value, + sortitionPool, + {from: bondCreator} + ) + + expectEvent(receipt, "BondCreated", { + operator: operator, + holder: holder, + sortitionPool: sortitionPool, + referenceID: reference, + amount: value, + }) + }) + + it("creates two bonds with the same reference for different operators", async () => { + const operator2 = accounts[6] + const authorizer2 = accounts[7] + const bondValue = new BN(10) + const reference = new BN(777) + + const expectedUnbonded = value.sub(bondValue) + + await stakeDelegatable.setBeneficiary(operator2, etherReceiver.address) + await stakeDelegatable.setAuthorizer(operator2, authorizer2) + + await abstractBonding.deposit(operator2, {value: value}) + + await authorizations.authorizeOperatorContract(operator2, bondCreator) + await abstractBonding.authorizeSortitionPoolContract( + operator2, + sortitionPool, + {from: authorizer2} + ) + await abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + await abstractBonding.createBond( + operator2, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + + const unbonded1 = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + expect(unbonded1).to.eq.BN(expectedUnbonded, "invalid unbonded value 1") + + const unbonded2 = await abstractBonding.availableUnbondedValue( + operator2, + bondCreator, + sortitionPool + ) + expect(unbonded2).to.eq.BN(expectedUnbonded, "invalid unbonded value 2") + + const lockedBonds1 = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds1).to.eq.BN(bondValue, "unexpected bond value 1") + + const lockedBonds2 = await abstractBonding.bondAmount( + operator2, + holder, + reference + ) + expect(lockedBonds2).to.eq.BN(bondValue, "unexpected bond value 2") + }) + + it("fails to create two bonds with the same reference for the same operator", async () => { + const bondValue = new BN(10) + const reference = new BN(777) + + await abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + + await expectRevert( + abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ), + "Reference ID not unique for holder and operator" + ) + }) + + it("fails if insufficient unbonded value", async () => { + const bondValue = value.add(new BN(1)) + + await expectRevert( + abstractBonding.createBond( + operator, + holder, + 0, + bondValue, + sortitionPool, + { + from: bondCreator, + } + ), + "Insufficient unbonded value" + ) + }) + }) + + describe("reassignBond", async () => { + const holder = accounts[6] + const newHolder = accounts[3] + const bondValue = new BN(100) + const reference = new BN(777) + const newReference = new BN(888) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: bondValue}) + await abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + }) + + it("reassigns bond to a new holder and a new reference", async () => { + await abstractBonding.reassignBond( + operator, + reference, + newHolder, + newReference, + {from: holder} + ) + + let lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") + + lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + newReference + ) + expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") + + lockedBonds = await abstractBonding.bondAmount( + operator, + newHolder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") + + lockedBonds = await abstractBonding.bondAmount( + operator, + newHolder, + newReference + ) + expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") + }) + + it("reassigns bond to the same holder and a new reference", async () => { + await abstractBonding.reassignBond( + operator, + reference, + holder, + newReference, + {from: holder} + ) + + let lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") + + lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + newReference + ) + expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") + }) + + it("reassigns bond to a new holder and the same reference", async () => { + await abstractBonding.reassignBond( + operator, + reference, + newHolder, + reference, + {from: holder} + ) + + let lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") + + lockedBonds = await abstractBonding.bondAmount( + operator, + newHolder, + reference + ) + expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") + }) + + it("emits event", async () => { + const receipt = await abstractBonding.reassignBond( + operator, + reference, + newHolder, + newReference, + {from: holder} + ) + + expectEvent(receipt, "BondReassigned", { + operator: operator, + referenceID: reference, + newHolder: newHolder, + newReferenceID: newReference, + }) + }) + + it("fails if sender is not the holder", async () => { + await expectRevert( + abstractBonding.reassignBond( + operator, + reference, + newHolder, + newReference, + { + from: accounts[0], + } + ), + "Bond not found" + ) + }) + + it("fails if reassigned to the same holder and the same reference", async () => { + await abstractBonding.deposit(operator, {value: bondValue}) + await abstractBonding.createBond( + operator, + holder, + newReference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + + await expectRevert( + abstractBonding.reassignBond( + operator, + reference, + holder, + newReference, + { + from: holder, + } + ), + "Reference ID not unique for holder and operator" + ) + }) + }) + + describe("freeBond", async () => { + const holder = accounts[6] + const initialUnboundedValue = new BN(500) + const bondValue = new BN(100) + const reference = new BN(777) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: initialUnboundedValue}) + await abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + }) + + it("releases bond amount to operator's available bonding value", async () => { + await abstractBonding.freeBond(operator, reference, {from: holder}) + + const lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "unexpected remaining locked bonds") + + const unbondedValue = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + expect(unbondedValue).to.eq.BN( + initialUnboundedValue, + "unexpected unbonded value" + ) + }) + + it("emits event", async () => { + const receipt = await abstractBonding.freeBond(operator, reference, { + from: holder, + }) + + expectEvent(receipt, "BondReleased", { + operator: operator, + referenceID: reference, + }) + }) + + it("fails if sender is not the holder", async () => { + await expectRevert( + abstractBonding.freeBond(operator, reference, {from: accounts[0]}), + "Bond not found" + ) + }) + }) + + describe("seizeBond", async () => { + const holder = accounts[6] + const destination = accounts[3] + const bondValue = new BN(1000) + const reference = new BN(777) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: bondValue}) + await abstractBonding.createBond( + operator, + holder, + reference, + bondValue, + sortitionPool, + {from: bondCreator} + ) + }) + + it("transfers whole bond amount to destination account", async () => { + const amount = bondValue + const expectedBalance = web3.utils + .toBN(await web3.eth.getBalance(destination)) + .add(amount) + + await abstractBonding.seizeBond( + operator, + reference, + amount, + destination, + { + from: holder, + } + ) + + const actualBalance = await web3.eth.getBalance(destination) + expect(actualBalance).to.eq.BN( + expectedBalance, + "invalid destination account balance" + ) + + const lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(0, "unexpected remaining bond value") + }) + + it("emits event", async () => { + const amount = new BN(80) + + const receipt = await abstractBonding.seizeBond( + operator, + reference, + amount, + destination, + { + from: holder, + } + ) + + expectEvent(receipt, "BondSeized", { + operator: operator, + referenceID: reference, + destination: destination, + amount: amount, + }) + }) + + it("transfers less than bond amount to destination account", async () => { + const remainingBond = new BN(1) + const amount = bondValue.sub(remainingBond) + const expectedBalance = web3.utils + .toBN(await web3.eth.getBalance(destination)) + .add(amount) + + await abstractBonding.seizeBond( + operator, + reference, + amount, + destination, + { + from: holder, + } + ) + + const actualBalance = await web3.eth.getBalance(destination) + expect(actualBalance).to.eq.BN( + expectedBalance, + "invalid destination account balance" + ) + + const lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN( + remainingBond, + "unexpected remaining bond value" + ) + }) + + it("reverts if seized amount equals zero", async () => { + const amount = new BN(0) + await expectRevert( + abstractBonding.seizeBond(operator, reference, amount, destination, { + from: holder, + }), + "Requested amount should be greater than zero" + ) + }) + + it("reverts if seized amount is greater than bond value", async () => { + const amount = bondValue.add(new BN(1)) + await expectRevert( + abstractBonding.seizeBond(operator, reference, amount, destination, { + from: holder, + }), + "Requested amount is greater than the bond" + ) + }) + + it("reverts if transfer fails", async () => { + await etherReceiver.setShouldFail(true) + const destination = etherReceiver.address + + await expectRevert( + abstractBonding.seizeBond(operator, reference, bondValue, destination, { + from: holder, + }), + "Transfer failed" + ) + + const destinationBalance = await web3.eth.getBalance(destination) + expect(destinationBalance).to.eq.BN( + 0, + "invalid destination account balance" + ) + + const lockedBonds = await abstractBonding.bondAmount( + operator, + holder, + reference + ) + expect(lockedBonds).to.eq.BN(bondValue, "unexpected bond value") + }) + }) + + describe("authorizeSortitionPoolContract", async () => { + it("reverts when operator is not an authorizer", async () => { + const authorizer1 = accounts[6] + + await expectRevert( + abstractBonding.authorizeSortitionPoolContract( + operator, + sortitionPool, + { + from: authorizer1, + } + ), + "Not authorized" + ) + }) + + it("should authorize sortition pool for the provided operator", async () => { + await abstractBonding.authorizeSortitionPoolContract( + operator, + sortitionPool, + {from: authorizer} + ) + + assert.isTrue( + await abstractBonding.hasSecondaryAuthorization( + operator, + sortitionPool + ), + "Sortition pool should be authorized for the provided operator" + ) + }) + }) + + describe("deauthorizeSortitionPoolContract", async () => { + it("reverts when operator is not an authorizer", async () => { + const authorizer1 = accounts[6] + + await expectRevert( + abstractBonding.deauthorizeSortitionPoolContract( + operator, + sortitionPool, + { + from: authorizer1, + } + ), + "Not authorized" + ) + }) + + it("should deauthorize sortition pool for the provided operator", async () => { + await abstractBonding.authorizeSortitionPoolContract( + operator, + sortitionPool, + {from: authorizer} + ) + await abstractBonding.deauthorizeSortitionPoolContract( + operator, + sortitionPool, + {from: authorizer} + ) + assert.isFalse( + await abstractBonding.hasSecondaryAuthorization( + operator, + sortitionPool + ), + "Sortition pool should be deauthorized for the provided operator" + ) + }) + + describe("withdrawBond", async () => { + const value = new BN(1000) + + beforeEach(async () => { + await stakeDelegatable.setBeneficiary(operator, beneficiary) + await abstractBonding.deposit(operator, {value: value}) + }) + + it("transfers unbonded value to beneficiary", async () => { + const expectedUnbonded = 0 + await stakeDelegatable.setBeneficiary(operator, beneficiary) + const expectedBeneficiaryBalance = web3.utils + .toBN(await web3.eth.getBalance(beneficiary)) + .add(value) + + await abstractBonding.withdrawBondExposed(value, operator, { + from: operator, + }) + + const unbonded = await abstractBonding.availableUnbondedValue( + operator, + bondCreator, + sortitionPool + ) + expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") + + const actualBeneficiaryBalance = await web3.eth.getBalance(beneficiary) + expect(actualBeneficiaryBalance).to.eq.BN( + expectedBeneficiaryBalance, + "invalid beneficiary balance" + ) + }) + + it("emits event", async () => { + const value = new BN(90) + + const receipt = await abstractBonding.withdrawBondExposed( + value, + operator, + { + from: operator, + } + ) + expectEvent(receipt, "UnbondedValueWithdrawn", { + operator: operator, + beneficiary: beneficiary, + amount: value, + }) + }) + + it("reverts if insufficient unbonded value", async () => { + const invalidValue = value.add(new BN(1)) + + await expectRevert( + abstractBonding.withdrawBondExposed(invalidValue, operator, { + from: operator, + }), + "Insufficient unbonded value" + ) + }) + + it("reverts if transfer fails", async () => { + await etherReceiver.setShouldFail(true) + await stakeDelegatable.setBeneficiary(operator, etherReceiver.address) + + await expectRevert( + abstractBonding.withdrawBondExposed(value, operator, { + from: operator, + }), + "Transfer failed" + ) + }) + }) + }) +}) diff --git a/solidity/test/KeepBondingTest.js b/solidity/test/KeepBondingTest.js index b8233311e..b221e8ed9 100644 --- a/solidity/test/KeepBondingTest.js +++ b/solidity/test/KeepBondingTest.js @@ -8,18 +8,13 @@ const ManagedGrant = contract.fromArtifact("ManagedGrantStub") const KeepBonding = contract.fromArtifact("KeepBonding") const TestEtherReceiver = contract.fromArtifact("TestEtherReceiver") -const { - constants, - expectEvent, - expectRevert, -} = require("@openzeppelin/test-helpers") +const {expectEvent, expectRevert} = require("@openzeppelin/test-helpers") const BN = web3.utils.BN const chai = require("chai") chai.use(require("bn-chai")(BN)) const expect = chai.expect -const assert = chai.assert describe("KeepBonding", function () { let registry @@ -52,6 +47,7 @@ describe("KeepBonding", function () { etherReceiver = await TestEtherReceiver.new() await registry.approveOperatorContract(bondCreator) + await keepBonding.authorizeSortitionPoolContract(operator, sortitionPool, { from: authorizer, }) @@ -67,42 +63,6 @@ describe("KeepBonding", function () { await restoreSnapshot() }) - describe("deposit", async () => { - const value = new BN(100) - const expectedUnbonded = value - - it("registers unbonded value", async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: value}) - const unbonded = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - sortitionPool - ) - - expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") - }) - - it("emits event", async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - const receipt = await keepBonding.deposit(operator, {value: value}) - expectEvent(receipt, "UnbondedValueDeposited", { - operator: operator, - beneficiary: beneficiary, - amount: value, - }) - }) - - it("reverts if beneficiary is not defined", async () => { - await tokenStaking.setBeneficiary(operator, constants.ZERO_ADDRESS) - - await expectRevert( - keepBonding.deposit(operator, {value: value}), - "Beneficiary not defined for the operator" - ) - }) - }) - describe("withdraw", async () => { const value = new BN(1000) @@ -351,612 +311,4 @@ describe("KeepBonding", function () { ) }) }) - - describe("availableUnbondedValue", async () => { - const value = new BN(100) - - beforeEach(async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: value}) - }) - - it("returns zero for operator with no deposit", async () => { - const unbondedOperator = "0x0000000000000000000000000000000000000001" - const expectedUnbonded = 0 - - const unbondedValue = await keepBonding.availableUnbondedValue( - unbondedOperator, - bondCreator, - sortitionPool - ) - expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") - }) - - it("return zero when bond creator is not approved by operator", async () => { - const notApprovedBondCreator = - "0x0000000000000000000000000000000000000001" - const expectedUnbonded = 0 - - const unbondedValue = await keepBonding.availableUnbondedValue( - operator, - notApprovedBondCreator, - sortitionPool - ) - expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") - }) - - it("returns zero when sortition pool is not authorized", async () => { - const notAuthorizedSortitionPool = - "0x0000000000000000000000000000000000000001" - const expectedUnbonded = 0 - - const unbondedValue = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - notAuthorizedSortitionPool - ) - expect(unbondedValue).to.eq.BN(expectedUnbonded, "invalid unbonded value") - }) - - it("returns value of operators deposit", async () => { - const expectedUnbonded = value - - const unbonded = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - sortitionPool - ) - - expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") - }) - }) - - describe("createBond", async () => { - const holder = accounts[3] - const value = new BN(100) - - beforeEach(async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: value}) - }) - - it("creates bond", async () => { - const reference = new BN(888) - - const expectedUnbonded = 0 - - await keepBonding.createBond( - operator, - holder, - reference, - value, - sortitionPool, - {from: bondCreator} - ) - - const unbonded = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - sortitionPool - ) - expect(unbonded).to.eq.BN(expectedUnbonded, "invalid unbonded value") - - const lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(value, "unexpected bond value") - }) - - it("emits event", async () => { - const reference = new BN(999) - - const receipt = await keepBonding.createBond( - operator, - holder, - reference, - value, - sortitionPool, - {from: bondCreator} - ) - - expectEvent(receipt, "BondCreated", { - operator: operator, - holder: holder, - sortitionPool: sortitionPool, - referenceID: reference, - amount: value, - }) - }) - - it("creates two bonds with the same reference for different operators", async () => { - const operator2 = accounts[2] - const authorizer2 = accounts[2] - const bondValue = new BN(10) - const reference = new BN(777) - - const expectedUnbonded = value.sub(bondValue) - - await tokenStaking.setBeneficiary(operator2, etherReceiver.address) - await keepBonding.deposit(operator2, {value: value}) - - await tokenStaking.authorizeOperatorContract(operator2, bondCreator) - await keepBonding.authorizeSortitionPoolContract( - operator2, - sortitionPool, - {from: authorizer2} - ) - await keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - await keepBonding.createBond( - operator2, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - - const unbonded1 = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - sortitionPool - ) - expect(unbonded1).to.eq.BN(expectedUnbonded, "invalid unbonded value 1") - - const unbonded2 = await keepBonding.availableUnbondedValue( - operator2, - bondCreator, - sortitionPool - ) - expect(unbonded2).to.eq.BN(expectedUnbonded, "invalid unbonded value 2") - - const lockedBonds1 = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds1).to.eq.BN(bondValue, "unexpected bond value 1") - - const lockedBonds2 = await keepBonding.bondAmount( - operator2, - holder, - reference - ) - expect(lockedBonds2).to.eq.BN(bondValue, "unexpected bond value 2") - }) - - it("fails to create two bonds with the same reference for the same operator", async () => { - const bondValue = new BN(10) - const reference = new BN(777) - - await keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - - await expectRevert( - keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ), - "Reference ID not unique for holder and operator" - ) - }) - - it("fails if insufficient unbonded value", async () => { - const bondValue = value.add(new BN(1)) - - await expectRevert( - keepBonding.createBond(operator, holder, 0, bondValue, sortitionPool, { - from: bondCreator, - }), - "Insufficient unbonded value" - ) - }) - }) - - describe("reassignBond", async () => { - const holder = accounts[2] - const newHolder = accounts[3] - const bondValue = new BN(100) - const reference = new BN(777) - const newReference = new BN(888) - - beforeEach(async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: bondValue}) - await keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - }) - - it("reassigns bond to a new holder and a new reference", async () => { - await keepBonding.reassignBond( - operator, - reference, - newHolder, - newReference, - {from: holder} - ) - - let lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") - - lockedBonds = await keepBonding.bondAmount(operator, holder, newReference) - expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") - - lockedBonds = await keepBonding.bondAmount(operator, newHolder, reference) - expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") - - lockedBonds = await keepBonding.bondAmount( - operator, - newHolder, - newReference - ) - expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") - }) - - it("reassigns bond to the same holder and a new reference", async () => { - await keepBonding.reassignBond( - operator, - reference, - holder, - newReference, - {from: holder} - ) - - let lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") - - lockedBonds = await keepBonding.bondAmount(operator, holder, newReference) - expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") - }) - - it("reassigns bond to a new holder and the same reference", async () => { - await keepBonding.reassignBond( - operator, - reference, - newHolder, - reference, - {from: holder} - ) - - let lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(0, "invalid locked bonds") - - lockedBonds = await keepBonding.bondAmount(operator, newHolder, reference) - expect(lockedBonds).to.eq.BN(bondValue, "invalid locked bonds") - }) - - it("emits event", async () => { - const receipt = await keepBonding.reassignBond( - operator, - reference, - newHolder, - newReference, - {from: holder} - ) - - expectEvent(receipt, "BondReassigned", { - operator: operator, - referenceID: reference, - newHolder: newHolder, - newReferenceID: newReference, - }) - }) - - it("fails if sender is not the holder", async () => { - await expectRevert( - keepBonding.reassignBond(operator, reference, newHolder, newReference, { - from: accounts[0], - }), - "Bond not found" - ) - }) - - it("fails if reassigned to the same holder and the same reference", async () => { - await keepBonding.deposit(operator, {value: bondValue}) - await keepBonding.createBond( - operator, - holder, - newReference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - - await expectRevert( - keepBonding.reassignBond(operator, reference, holder, newReference, { - from: holder, - }), - "Reference ID not unique for holder and operator" - ) - }) - }) - - describe("freeBond", async () => { - const holder = accounts[2] - const initialUnboundedValue = new BN(500) - const bondValue = new BN(100) - const reference = new BN(777) - - beforeEach(async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: initialUnboundedValue}) - await keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - }) - - it("releases bond amount to operator's available bonding value", async () => { - await keepBonding.freeBond(operator, reference, {from: holder}) - - const lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(0, "unexpected remaining locked bonds") - - const unbondedValue = await keepBonding.availableUnbondedValue( - operator, - bondCreator, - sortitionPool - ) - expect(unbondedValue).to.eq.BN( - initialUnboundedValue, - "unexpected unbonded value" - ) - }) - - it("emits event", async () => { - const receipt = await keepBonding.freeBond(operator, reference, { - from: holder, - }) - - expectEvent(receipt, "BondReleased", { - operator: operator, - referenceID: reference, - }) - }) - - it("fails if sender is not the holder", async () => { - await expectRevert( - keepBonding.freeBond(operator, reference, {from: accounts[0]}), - "Bond not found" - ) - }) - }) - - describe("seizeBond", async () => { - const holder = accounts[2] - const destination = accounts[3] - const bondValue = new BN(1000) - const reference = new BN(777) - - beforeEach(async () => { - await tokenStaking.setBeneficiary(operator, beneficiary) - await keepBonding.deposit(operator, {value: bondValue}) - await keepBonding.createBond( - operator, - holder, - reference, - bondValue, - sortitionPool, - {from: bondCreator} - ) - }) - - it("transfers whole bond amount to destination account", async () => { - const amount = bondValue - const expectedBalance = web3.utils - .toBN(await web3.eth.getBalance(destination)) - .add(amount) - - await keepBonding.seizeBond(operator, reference, amount, destination, { - from: holder, - }) - - const actualBalance = await web3.eth.getBalance(destination) - expect(actualBalance).to.eq.BN( - expectedBalance, - "invalid destination account balance" - ) - - const lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(0, "unexpected remaining bond value") - }) - - it("emits event", async () => { - const amount = new BN(80) - - const receipt = await keepBonding.seizeBond( - operator, - reference, - amount, - destination, - { - from: holder, - } - ) - - expectEvent(receipt, "BondSeized", { - operator: operator, - referenceID: reference, - destination: destination, - amount: amount, - }) - }) - - it("transfers less than bond amount to destination account", async () => { - const remainingBond = new BN(1) - const amount = bondValue.sub(remainingBond) - const expectedBalance = web3.utils - .toBN(await web3.eth.getBalance(destination)) - .add(amount) - - await keepBonding.seizeBond(operator, reference, amount, destination, { - from: holder, - }) - - const actualBalance = await web3.eth.getBalance(destination) - expect(actualBalance).to.eq.BN( - expectedBalance, - "invalid destination account balance" - ) - - const lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN( - remainingBond, - "unexpected remaining bond value" - ) - }) - - it("reverts if seized amount equals zero", async () => { - const amount = new BN(0) - await expectRevert( - keepBonding.seizeBond(operator, reference, amount, destination, { - from: holder, - }), - "Requested amount should be greater than zero" - ) - }) - - it("reverts if seized amount is greater than bond value", async () => { - const amount = bondValue.add(new BN(1)) - await expectRevert( - keepBonding.seizeBond(operator, reference, amount, destination, { - from: holder, - }), - "Requested amount is greater than the bond" - ) - }) - - it("reverts if transfer fails", async () => { - await etherReceiver.setShouldFail(true) - const destination = etherReceiver.address - - await expectRevert( - keepBonding.seizeBond(operator, reference, bondValue, destination, { - from: holder, - }), - "Transfer failed" - ) - - const destinationBalance = await web3.eth.getBalance(destination) - expect(destinationBalance).to.eq.BN( - 0, - "invalid destination account balance" - ) - - const lockedBonds = await keepBonding.bondAmount( - operator, - holder, - reference - ) - expect(lockedBonds).to.eq.BN(bondValue, "unexpected bond value") - }) - }) - - describe("authorizeSortitionPoolContract", async () => { - it("reverts when operator is not an authorizer", async () => { - const authorizer1 = accounts[2] - - await expectRevert( - keepBonding.authorizeSortitionPoolContract(operator, sortitionPool, { - from: authorizer1, - }), - "Not authorized" - ) - }) - - it("should authorize sortition pool for the provided operator", async () => { - await keepBonding.authorizeSortitionPoolContract( - operator, - sortitionPool, - {from: authorizer} - ) - - assert.isTrue( - await keepBonding.hasSecondaryAuthorization(operator, sortitionPool), - "Sortition pool should be authorized for the provided operator" - ) - }) - }) - - describe("deauthorizeSortitionPoolContract", async () => { - it("reverts when operator is not an authorizer", async () => { - const authorizer1 = accounts[2] - - await expectRevert( - keepBonding.deauthorizeSortitionPoolContract(operator, sortitionPool, { - from: authorizer1, - }), - "Not authorized" - ) - }) - - it("should deauthorize sortition pool for the provided operator", async () => { - await keepBonding.authorizeSortitionPoolContract( - operator, - sortitionPool, - {from: authorizer} - ) - await keepBonding.deauthorizeSortitionPoolContract( - operator, - sortitionPool, - {from: authorizer} - ) - assert.isFalse( - await keepBonding.hasSecondaryAuthorization(operator, sortitionPool), - "Sortition pool should be deauthorized for the provided operator" - ) - }) - }) }) From 266afb997e3937d56824a91c8114699cc926cb5c Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Aug 2020 22:12:21 +0200 Subject: [PATCH 4/9] AbstractBonding declares abstract functions for TokenStaking We added abstract functions for the calls we expect to make to staking contract. For KEEP token staking we expect calls to TokenStaking and for ETH staking calls to dedicated staking contract. We no longer need to pass two addresses: Authorizations and StakeDelegatable contract which seemed to be confusing. --- solidity/contracts/AbstractBonding.sol | 57 +++++++++++++------------- solidity/contracts/KeepBonding.sol | 38 +++++++++++------ 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/solidity/contracts/AbstractBonding.sol b/solidity/contracts/AbstractBonding.sol index 3d22fe0df..f1473f7bb 100644 --- a/solidity/contracts/AbstractBonding.sol +++ b/solidity/contracts/AbstractBonding.sol @@ -15,9 +15,8 @@ pragma solidity 0.5.17; import "@keep-network/keep-core/contracts/KeepRegistry.sol"; -import "@keep-network/keep-core/contracts/Authorizations.sol"; -import "@keep-network/keep-core/contracts/StakeDelegatable.sol"; import "@keep-network/sortition-pools/contracts/api/IBonding.sol"; + import "openzeppelin-solidity/contracts/math/SafeMath.sol"; @@ -29,12 +28,6 @@ contract AbstractBonding is IBonding { // Registry contract with a list of approved factories (operator contracts). KeepRegistry internal registry; - // Staking Authorizations contract. - Authorizations internal authorizations; - - // Stake Delegatable contract. - StakeDelegatable internal stakeDelegatable; - // Unassigned value in wei deposited by operators. mapping(address => uint256) public unbondedValue; @@ -79,22 +72,14 @@ contract AbstractBonding is IBonding { /// @notice Initializes Keep Bonding contract. /// @param registryAddress Keep registry contract address. - /// @param authorizationsAddress Staking Authorizations contract address. - /// @param stakeDelegatableAddress Stake Delegatable contract address. - constructor( - address registryAddress, - address authorizationsAddress, - address stakeDelegatableAddress - ) public { + constructor(address registryAddress) public { registry = KeepRegistry(registryAddress); - authorizations = Authorizations(authorizationsAddress); - stakeDelegatable = StakeDelegatable(stakeDelegatableAddress); } /// @notice Add the provided value to operator's pool available for bonding. /// @param operator Address of the operator. function deposit(address operator) external payable { - address beneficiary = stakeDelegatable.beneficiaryOf(operator); + address beneficiary = beneficiaryOf(operator); // Beneficiary has to be set (delegation exist) before an operator can // deposit wei. It protects from a situation when an operator wants // to withdraw funds which are transfered to beneficiary with zero @@ -131,7 +116,7 @@ contract AbstractBonding is IBonding { // are no longer eligible. We cannot revert here. if ( registry.isApprovedOperatorContract(bondCreator) && - authorizations.isAuthorizedForOperator(operator, bondCreator) && + isAuthorizedForOperator(operator, bondCreator) && hasSecondaryAuthorization(operator, authorizedSortitionPool) ) { return unbondedValue[operator]; @@ -301,10 +286,7 @@ contract AbstractBonding is IBonding { address _operator, address _poolAddress ) public { - require( - stakeDelegatable.authorizerOf(_operator) == msg.sender, - "Not authorized" - ); + require(authorizerOf(_operator) == msg.sender, "Not authorized"); authorizedPools[_operator][_poolAddress] = true; } @@ -318,10 +300,7 @@ contract AbstractBonding is IBonding { address _operator, address _poolAddress ) public { - require( - stakeDelegatable.authorizerOf(_operator) == msg.sender, - "Not authorized" - ); + require(authorizerOf(_operator) == msg.sender, "Not authorized"); authorizedPools[_operator][_poolAddress] = false; } @@ -347,11 +326,33 @@ contract AbstractBonding is IBonding { unbondedValue[operator] = unbondedValue[operator].sub(amount); - address beneficiary = stakeDelegatable.beneficiaryOf(operator); + address beneficiary = beneficiaryOf(operator); (bool success, ) = beneficiary.call.value(amount)(""); require(success, "Transfer failed"); emit UnbondedValueWithdrawn(operator, beneficiary, amount); } + + /// @notice Checks if operator contract has been authorized for the provided + /// operator. + /// @param _operator Operator address. + /// @param _operatorContract Address of the operator contract. + function isAuthorizedForOperator( + address _operator, + address _operatorContract + ) internal view returns (bool); + + /// @notice Gets the authorizer for the specified operator address. + /// @param _operator Operator address. + /// @return Authorizer address. + function authorizerOf(address _operator) internal view returns (address); + + /// @notice Gets the beneficiary for the specified operator address. + /// @param _operator Operator address. + /// @return Beneficiary address. + function beneficiaryOf(address _operator) + internal + view + returns (address payable); } diff --git a/solidity/contracts/KeepBonding.sol b/solidity/contracts/KeepBonding.sol index a0b2dddec..bb26b2b3b 100644 --- a/solidity/contracts/KeepBonding.sol +++ b/solidity/contracts/KeepBonding.sol @@ -25,6 +25,9 @@ import "@keep-network/keep-core/contracts/libraries/RolesLookup.sol"; contract KeepBonding is AbstractBonding { using RolesLookup for address payable; + // KEEP Token Staking contract. + TokenStaking internal tokenStaking; + // KEEP token grant contract. TokenGrant internal tokenGrant; @@ -36,14 +39,8 @@ contract KeepBonding is AbstractBonding { address registryAddress, address tokenStakingAddress, address tokenGrantAddress - ) - public - AbstractBonding( - registryAddress, - tokenStakingAddress, // Authorizations - tokenStakingAddress // StakeDelegatable - ) - { + ) public AbstractBonding(registryAddress) { + tokenStaking = TokenStaking(tokenStakingAddress); tokenGrant = TokenGrant(tokenGrantAddress); } @@ -61,10 +58,7 @@ contract KeepBonding is AbstractBonding { function withdraw(uint256 amount, address operator) public { require( msg.sender == operator || - msg.sender.isTokenOwnerForOperator( - operator, - stakeDelegatable - ) || + msg.sender.isTokenOwnerForOperator(operator, tokenStaking) || msg.sender.isGranteeForOperator(operator, tokenGrant), "Only operator or the owner is allowed to withdraw bond" ); @@ -93,4 +87,24 @@ contract KeepBonding is AbstractBonding { withdrawBond(amount, operator); } + + function isAuthorizedForOperator( + address _operator, + address _operatorContract + ) internal view returns (bool) { + return + tokenStaking.isAuthorizedForOperator(_operator, _operatorContract); + } + + function authorizerOf(address _operator) internal view returns (address) { + return tokenStaking.authorizerOf(_operator); + } + + function beneficiaryOf(address _operator) + internal + view + returns (address payable) + { + return tokenStaking.beneficiaryOf(_operator); + } } From 5f2194fcc1453b5760a2c7e97bf4c4b1e101f75c Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Aug 2020 22:15:38 +0200 Subject: [PATCH 5/9] Updated unit tests stubs for abstract staking functions Updated unit tests stubs to handle functions added to AbstractBonding contract that should implement calls to staking contract holding informations about delgation. --- .../contracts/test/AbstractBondingStub.sol | 43 ++++++++++---- .../contracts/test/AuthorizationsStub.sol | 38 ------------- ...elegetableStub.sol => StakingInfoStub.sol} | 39 +++++++------ solidity/contracts/test/TokenStakingStub.sol | 57 +++---------------- solidity/test/AbstractBondingTest.js | 45 +++++++-------- solidity/test/BondedECDSAKeepFactoryTest.js | 4 ++ solidity/test/BondedECDSAKeepTest.js | 4 ++ solidity/test/KeepBondingTest.js | 1 + 8 files changed, 90 insertions(+), 141 deletions(-) delete mode 100644 solidity/contracts/test/AuthorizationsStub.sol rename solidity/contracts/test/{StakeDelegetableStub.sol => StakingInfoStub.sol} (67%) diff --git a/solidity/contracts/test/AbstractBondingStub.sol b/solidity/contracts/test/AbstractBondingStub.sol index 7a6faed7b..69a5d39b6 100644 --- a/solidity/contracts/test/AbstractBondingStub.sol +++ b/solidity/contracts/test/AbstractBondingStub.sol @@ -2,19 +2,17 @@ pragma solidity 0.5.17; import "../../contracts/AbstractBonding.sol"; +import "./StakingInfoStub.sol"; + contract AbstractBondingStub is AbstractBonding { - constructor( - address registryAddress, - address authorizationsAddress, - address stakeDelegatableAddress - ) + StakingInfoStub stakingInfoStub; + + constructor(address registryAddress, address stakingInfoAddress) public - AbstractBonding( - registryAddress, - authorizationsAddress, - stakeDelegatableAddress - ) - {} + AbstractBonding(registryAddress) + { + stakingInfoStub = StakingInfoStub(stakingInfoAddress); + } function withdraw(uint256 amount, address operator) public { revert("abstract function"); @@ -23,4 +21,27 @@ contract AbstractBondingStub is AbstractBonding { function withdrawBondExposed(uint256 amount, address operator) public { withdrawBond(amount, operator); } + + function isAuthorizedForOperator( + address _operator, + address _operatorContract + ) internal view returns (bool) { + return + stakingInfoStub.isAuthorizedForOperator( + _operator, + _operatorContract + ); + } + + function authorizerOf(address _operator) internal view returns (address) { + return stakingInfoStub.authorizerOf(_operator); + } + + function beneficiaryOf(address _operator) + internal + view + returns (address payable) + { + return stakingInfoStub.beneficiaryOf(_operator); + } } diff --git a/solidity/contracts/test/AuthorizationsStub.sol b/solidity/contracts/test/AuthorizationsStub.sol deleted file mode 100644 index f666250c6..000000000 --- a/solidity/contracts/test/AuthorizationsStub.sol +++ /dev/null @@ -1,38 +0,0 @@ -pragma solidity 0.5.17; - -import "@keep-network/keep-core/contracts/Authorizations.sol"; -import "@keep-network/keep-core/contracts/KeepRegistry.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -/// @title Authorizations Stub -/// @dev This contract is for testing purposes only. -contract AuthorizationsStub is Authorizations { - // Authorized operator contracts. - mapping(address => mapping(address => bool)) internal authorizations; - - address public delegatedAuthority; - - constructor(KeepRegistry _registry) public Authorizations(_registry) {} - - function authorizeOperatorContract( - address _operator, - address _operatorContract - ) public { - authorizations[_operatorContract][_operator] = true; - } - - function isAuthorizedForOperator( - address _operator, - address _operatorContract - ) public view returns (bool) { - return authorizations[_operatorContract][_operator]; - } - - function authorizerOf(address _operator) public view returns (address) { - revert("abstract function"); - } - - function claimDelegatedAuthority(address delegatedAuthoritySource) public { - delegatedAuthority = delegatedAuthoritySource; - } -} diff --git a/solidity/contracts/test/StakeDelegetableStub.sol b/solidity/contracts/test/StakingInfoStub.sol similarity index 67% rename from solidity/contracts/test/StakeDelegetableStub.sol rename to solidity/contracts/test/StakingInfoStub.sol index f868c3159..78fd59a3e 100644 --- a/solidity/contracts/test/StakeDelegetableStub.sol +++ b/solidity/contracts/test/StakingInfoStub.sol @@ -1,30 +1,27 @@ pragma solidity 0.5.17; -import "@keep-network/keep-core/contracts/StakeDelegatable.sol"; - -/// @title Stake Delegatable Stub +/// @title Staking Stub /// @dev This contract is for testing purposes only. -contract StakeDelegatableStub is StakeDelegatable { - mapping(address => uint256) stakes; +contract StakingInfoStub { + // Authorized operator contracts. + mapping(address => mapping(address => bool)) internal authorizations; mapping(address => address) operatorToOwner; mapping(address => address payable) operatorToBeneficiary; mapping(address => address) operatorToAuthorizer; - function setBalance(address _operator, uint256 _balance) public { - stakes[_operator] = _balance; - } - - function balanceOf(address _address) public view returns (uint256 balance) { - return stakes[_address]; + function authorizeOperatorContract( + address _operator, + address _operatorContract + ) public { + authorizations[_operatorContract][_operator] = true; } - function setOwner(address _operator, address _owner) public { - operatorToOwner[_operator] = _owner; - } - - function ownerOf(address _operator) public view returns (address) { - return operatorToOwner[_operator]; + function isAuthorizedForOperator( + address _operator, + address _operatorContract + ) public view returns (bool) { + return authorizations[_operatorContract][_operator]; } function setBeneficiary(address _operator, address payable _beneficiary) @@ -48,4 +45,12 @@ contract StakeDelegatableStub is StakeDelegatable { function authorizerOf(address _operator) public view returns (address) { return operatorToAuthorizer[_operator]; } + + function setOwner(address _operator, address _owner) public { + operatorToOwner[_operator] = _owner; + } + + function ownerOf(address _operator) public view returns (address) { + return operatorToOwner[_operator]; + } } diff --git a/solidity/contracts/test/TokenStakingStub.sol b/solidity/contracts/test/TokenStakingStub.sol index ddc5d0d6e..accae7dcf 100644 --- a/solidity/contracts/test/TokenStakingStub.sol +++ b/solidity/contracts/test/TokenStakingStub.sol @@ -1,28 +1,21 @@ pragma solidity 0.5.17; +import "./StakingInfoStub.sol"; + import "@keep-network/sortition-pools/contracts/api/IStaking.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - /// @title Token Staking Stub /// @dev This contract is for testing purposes only. -contract TokenStakingStub is IStaking { +contract TokenStakingStub is IStaking, StakingInfoStub { using SafeMath for uint256; uint256 public minimumStake = 200000 * 1e18; - mapping(address => address payable) operatorToBeneficiary; - mapping(address => uint256) stakes; mapping(address => int256) public operatorLocks; - // Authorized operator contracts. - mapping(address => mapping(address => bool)) internal authorizations; - - // Map of operator -> owner. - mapping(address => address) owners; - address public delegatedAuthority; bool slashingShouldFail; @@ -36,6 +29,10 @@ contract TokenStakingStub is IStaking { stakes[_operator] = _balance; } + function balanceOf(address _address) public view returns (uint256 balance) { + return stakes[_address]; + } + /// @dev Returns balance variable value. function eligibleStake(address _operator, address) public @@ -45,20 +42,6 @@ contract TokenStakingStub is IStaking { return stakes[_operator]; } - function setBeneficiary(address _operator, address payable _beneficiary) - public - { - operatorToBeneficiary[_operator] = _beneficiary; - } - - function beneficiaryOf(address _operator) - public - view - returns (address payable) - { - return operatorToBeneficiary[_operator]; - } - function slash(uint256 _amount, address[] memory _misbehavedOperators) public { @@ -82,32 +65,6 @@ contract TokenStakingStub is IStaking { operatorLocks[operator] = -1; } - function authorizeOperatorContract( - address _operator, - address _operatorContract - ) public { - authorizations[_operatorContract][_operator] = true; - } - - function isAuthorizedForOperator( - address _operator, - address _operatorContract - ) public view returns (bool) { - return authorizations[_operatorContract][_operator]; - } - - function authorizerOf(address _operator) public view returns (address) { - return _operator; - } - - function setOwner(address _operator, address _owner) public { - owners[_operator] = _owner; - } - - function ownerOf(address _operator) public view returns (address) { - return owners[_operator]; - } - function claimDelegatedAuthority(address delegatedAuthoritySource) public { delegatedAuthority = delegatedAuthoritySource; } diff --git a/solidity/test/AbstractBondingTest.js b/solidity/test/AbstractBondingTest.js index a2624b4ce..4915450fa 100644 --- a/solidity/test/AbstractBondingTest.js +++ b/solidity/test/AbstractBondingTest.js @@ -2,8 +2,7 @@ const {accounts, contract, web3} = require("@openzeppelin/test-environment") const {createSnapshot, restoreSnapshot} = require("./helpers/snapshot") const KeepRegistry = contract.fromArtifact("KeepRegistry") -const AuthorizationsStub = contract.fromArtifact("AuthorizationsStub") -const StakeDelegatableStub = contract.fromArtifact("StakeDelegatableStub") +const StakingInfoStub = contract.fromArtifact("StakingInfoStub") const AbstractBonding = contract.fromArtifact("AbstractBondingStub") const TestEtherReceiver = contract.fromArtifact("TestEtherReceiver") @@ -22,7 +21,6 @@ const assert = chai.assert describe("AbstractBonding", function () { let registry - let stakeDelegatable let abstractBonding let etherReceiver @@ -40,21 +38,18 @@ describe("AbstractBonding", function () { sortitionPool = accounts[5] registry = await KeepRegistry.new() - authorizations = await AuthorizationsStub.new(registry.address) - - stakeDelegatable = await StakeDelegatableStub.new() + stakingInfoStub = await StakingInfoStub.new() abstractBonding = await AbstractBonding.new( registry.address, - authorizations.address, - stakeDelegatable.address + stakingInfoStub.address ) etherReceiver = await TestEtherReceiver.new() await registry.approveOperatorContract(bondCreator) - await stakeDelegatable.setAuthorizer(operator, authorizer) + await stakingInfoStub.setAuthorizer(operator, authorizer) await abstractBonding.authorizeSortitionPoolContract( operator, sortitionPool, @@ -63,7 +58,7 @@ describe("AbstractBonding", function () { } ) - await authorizations.authorizeOperatorContract(operator, bondCreator) + await stakingInfoStub.authorizeOperatorContract(operator, bondCreator) }) beforeEach(async () => { @@ -79,7 +74,7 @@ describe("AbstractBonding", function () { const expectedUnbonded = value it("registers unbonded value", async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: value}) const unbonded = await abstractBonding.availableUnbondedValue( operator, @@ -94,7 +89,7 @@ describe("AbstractBonding", function () { const value1 = value const value2 = new BN(230) - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: value1}) expect( @@ -119,7 +114,7 @@ describe("AbstractBonding", function () { }) it("emits event", async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) const receipt = await abstractBonding.deposit(operator, {value: value}) expectEvent(receipt, "UnbondedValueDeposited", { operator: operator, @@ -129,7 +124,7 @@ describe("AbstractBonding", function () { }) it("reverts if beneficiary is not defined", async () => { - await stakeDelegatable.setBeneficiary(operator, constants.ZERO_ADDRESS) + await stakingInfoStub.setBeneficiary(operator, constants.ZERO_ADDRESS) await expectRevert( abstractBonding.deposit(operator, {value: value}), @@ -142,7 +137,7 @@ describe("AbstractBonding", function () { const value = new BN(100) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: value}) }) @@ -202,7 +197,7 @@ describe("AbstractBonding", function () { const value = new BN(100) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: value}) }) @@ -264,12 +259,12 @@ describe("AbstractBonding", function () { const expectedUnbonded = value.sub(bondValue) - await stakeDelegatable.setBeneficiary(operator2, etherReceiver.address) - await stakeDelegatable.setAuthorizer(operator2, authorizer2) + await stakingInfoStub.setBeneficiary(operator2, etherReceiver.address) + await stakingInfoStub.setAuthorizer(operator2, authorizer2) await abstractBonding.deposit(operator2, {value: value}) - await authorizations.authorizeOperatorContract(operator2, bondCreator) + await stakingInfoStub.authorizeOperatorContract(operator2, bondCreator) await abstractBonding.authorizeSortitionPoolContract( operator2, sortitionPool, @@ -374,7 +369,7 @@ describe("AbstractBonding", function () { const newReference = new BN(888) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: bondValue}) await abstractBonding.createBond( operator, @@ -537,7 +532,7 @@ describe("AbstractBonding", function () { const reference = new BN(777) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: initialUnboundedValue}) await abstractBonding.createBond( operator, @@ -596,7 +591,7 @@ describe("AbstractBonding", function () { const reference = new BN(777) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: bondValue}) await abstractBonding.createBond( operator, @@ -812,13 +807,13 @@ describe("AbstractBonding", function () { const value = new BN(1000) beforeEach(async () => { - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) await abstractBonding.deposit(operator, {value: value}) }) it("transfers unbonded value to beneficiary", async () => { const expectedUnbonded = 0 - await stakeDelegatable.setBeneficiary(operator, beneficiary) + await stakingInfoStub.setBeneficiary(operator, beneficiary) const expectedBeneficiaryBalance = web3.utils .toBN(await web3.eth.getBalance(beneficiary)) .add(value) @@ -871,7 +866,7 @@ describe("AbstractBonding", function () { it("reverts if transfer fails", async () => { await etherReceiver.setShouldFail(true) - await stakeDelegatable.setBeneficiary(operator, etherReceiver.address) + await stakingInfoStub.setBeneficiary(operator, etherReceiver.address) await expectRevert( abstractBonding.withdrawBondExposed(value, operator, { diff --git a/solidity/test/BondedECDSAKeepFactoryTest.js b/solidity/test/BondedECDSAKeepFactoryTest.js index 96b68a7c1..d12d19d8d 100644 --- a/solidity/test/BondedECDSAKeepFactoryTest.js +++ b/solidity/test/BondedECDSAKeepFactoryTest.js @@ -1182,6 +1182,8 @@ describe("BondedECDSAKeepFactory", function () { }) await tokenStaking.setBalance(operator, stakeBalance) + await tokenStaking.setAuthorizer(operator, operator) + await tokenStaking.authorizeOperatorContract( operator, keepFactory.address @@ -1575,6 +1577,8 @@ describe("BondedECDSAKeepFactory", function () { await keepFactory.createSortitionPool(application) for (let i = 0; i < members.length; i++) { + await tokenStaking.setAuthorizer(members[i], authorizers[i]) + await tokenStaking.authorizeOperatorContract( members[i], keepFactory.address diff --git a/solidity/test/BondedECDSAKeepTest.js b/solidity/test/BondedECDSAKeepTest.js index c3b5b2536..31362f4df 100644 --- a/solidity/test/BondedECDSAKeepTest.js +++ b/solidity/test/BondedECDSAKeepTest.js @@ -107,6 +107,10 @@ describe("BondedECDSAKeep", function () { memberStake = await factoryStub.minimumStake.call() await stakeOperators(members, memberStake) + await tokenStaking.setAuthorizer(members[0], authorizers[0]) + await tokenStaking.setAuthorizer(members[1], authorizers[1]) + await tokenStaking.setAuthorizer(members[2], authorizers[2]) + await keepBonding.authorizeSortitionPoolContract(members[0], signingPool, { from: authorizers[0], }) diff --git a/solidity/test/KeepBondingTest.js b/solidity/test/KeepBondingTest.js index b221e8ed9..06716a130 100644 --- a/solidity/test/KeepBondingTest.js +++ b/solidity/test/KeepBondingTest.js @@ -48,6 +48,7 @@ describe("KeepBonding", function () { await registry.approveOperatorContract(bondCreator) + await tokenStaking.setAuthorizer(operator, authorizer) await keepBonding.authorizeSortitionPoolContract(operator, sortitionPool, { from: authorizer, }) From 0befc5c9f84472c195a200cd13ced98f2d1d0ca6 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Aug 2020 23:45:08 +0200 Subject: [PATCH 6/9] Fixed linting problems with indentation Solium complains about indentation problems but indentation is fine. Indentation is verified and fixed by prettier so we can ignore indentation cheks from oslium. --- solidity/.soliumrc.json | 1 + solidity/contracts/AbstractBonding.sol | 11 +- solidity/contracts/BondedECDSAKeep.sol | 10 +- solidity/contracts/BondedECDSAKeepFactory.sol | 1 - solidity/contracts/BondedECDSAKeepVendor.sol | 16 +- .../contracts/BondedECDSAKeepVendorImplV1.sol | 1 - solidity/contracts/CloneFactory.sol | 1 - solidity/contracts/KeepBonding.sol | 1 - solidity/contracts/Migrations.sol | 1 - solidity/package-lock.json | 545 +++++++++--------- solidity/package.json | 6 +- 11 files changed, 298 insertions(+), 296 deletions(-) diff --git a/solidity/.soliumrc.json b/solidity/.soliumrc.json index 5c7001462..954839637 100644 --- a/solidity/.soliumrc.json +++ b/solidity/.soliumrc.json @@ -10,6 +10,7 @@ "timestamp" ] ], + "indentation": "off", "security/no-call-value": "off", "security/no-inline-assembly": "off" } diff --git a/solidity/contracts/AbstractBonding.sol b/solidity/contracts/AbstractBonding.sol index f1473f7bb..4ebfdc52e 100644 --- a/solidity/contracts/AbstractBonding.sol +++ b/solidity/contracts/AbstractBonding.sol @@ -19,7 +19,6 @@ import "@keep-network/sortition-pools/contracts/api/IBonding.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - /// @title Keep Bonding /// @notice Contract holding deposits from keeps' operators. contract AbstractBonding is IBonding { @@ -175,11 +174,11 @@ contract AbstractBonding is IBonding { /// @param holder Address of the holder of the bond. /// @param referenceID Reference ID of the bond. /// @return Amount of wei in the selected bond. - function bondAmount(address operator, address holder, uint256 referenceID) - public - view - returns (uint256) - { + function bondAmount( + address operator, + address holder, + uint256 referenceID + ) public view returns (uint256) { bytes32 bondID = keccak256( abi.encodePacked(operator, holder, referenceID) ); diff --git a/solidity/contracts/BondedECDSAKeep.sol b/solidity/contracts/BondedECDSAKeep.sol index b03a60246..fc1f75c37 100644 --- a/solidity/contracts/BondedECDSAKeep.sol +++ b/solidity/contracts/BondedECDSAKeep.sol @@ -25,7 +25,6 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol"; - /// @title Bonded ECDSA Keep /// @notice ECDSA keep with additional signer bond requirement. /// @dev This contract is used as a master contract for clone factory in @@ -251,10 +250,11 @@ contract BondedECDSAKeep is IBondedECDSAKeep { /// @param _r Calculated signature's R value. /// @param _s Calculated signature's S value. /// @param _recoveryID Calculated signature's recovery ID (one of {0, 1, 2, 3}). - function submitSignature(bytes32 _r, bytes32 _s, uint8 _recoveryID) - external - onlyMember - { + function submitSignature( + bytes32 _r, + bytes32 _s, + uint8 _recoveryID + ) external onlyMember { require(isSigningInProgress(), "Not awaiting a signature"); require(_recoveryID < 4, "Recovery ID must be one of {0, 1, 2, 3}"); diff --git a/solidity/contracts/BondedECDSAKeepFactory.sol b/solidity/contracts/BondedECDSAKeepFactory.sol index 81c477b14..b107a0299 100644 --- a/solidity/contracts/BondedECDSAKeepFactory.sol +++ b/solidity/contracts/BondedECDSAKeepFactory.sol @@ -33,7 +33,6 @@ import "@keep-network/keep-core/contracts/utils/AddressArrayUtils.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - /// @title Bonded ECDSA Keep Factory /// @notice Contract creating bonded ECDSA keeps. /// @dev We avoid redeployment of bonded ECDSA keep contract by using the clone factory. diff --git a/solidity/contracts/BondedECDSAKeepVendor.sol b/solidity/contracts/BondedECDSAKeepVendor.sol index ed2f28b4e..c103a1f5e 100644 --- a/solidity/contracts/BondedECDSAKeepVendor.sol +++ b/solidity/contracts/BondedECDSAKeepVendor.sol @@ -17,7 +17,6 @@ pragma solidity 0.5.17; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "@openzeppelin/upgrades/contracts/upgradeability/Proxy.sol"; - /// @title Proxy contract for Bonded ECDSA Keep vendor. contract BondedECDSAKeepVendor is Proxy { using SafeMath for uint256; @@ -25,28 +24,33 @@ contract BondedECDSAKeepVendor is Proxy { /// @dev Storage slot with the admin of the contract. /// This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is /// validated in the constructor. - bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + bytes32 + internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /// @dev Storage slot with the address of the current implementation. /// This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is /// validated in the constructor. - bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 + internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /// @dev Storage slot with the upgrade time delay. Upgrade time delay defines a /// period for implementation upgrade. /// This is the keccak-256 hash of "network.keep.bondedecdsavendor.proxy.upgradeTimeDelay" /// subtracted by 1, and is validated in the constructor. - bytes32 internal constant UPGRADE_TIME_DELAY_SLOT = 0x3ca583dafde9ce8bdb41fe825f85984a83b08ecf90ffaccbc4b049e8d8703563; + bytes32 + internal constant UPGRADE_TIME_DELAY_SLOT = 0x3ca583dafde9ce8bdb41fe825f85984a83b08ecf90ffaccbc4b049e8d8703563; /// @dev Storage slot with the new implementation address. /// This is the keccak-256 hash of "network.keep.bondedecdsavendor.proxy.upgradeImplementation" /// subtracted by 1, and is validated in the constructor. - bytes32 internal constant UPGRADE_IMPLEMENTATION_SLOT = 0x4e06287250f0fdd90b4a096f346c06d4e706d470a14747ab56a0156d48a6883f; + bytes32 + internal constant UPGRADE_IMPLEMENTATION_SLOT = 0x4e06287250f0fdd90b4a096f346c06d4e706d470a14747ab56a0156d48a6883f; /// @dev Storage slot with the implementation address upgrade initiation. /// This is the keccak-256 hash of "network.keep.bondedecdsavendor.proxy.upgradeInitiatedTimestamp" /// subtracted by 1, and is validated in the constructor. - bytes32 internal constant UPGRADE_INIT_TIMESTAMP_SLOT = 0x0816e8d9eeb2554df0d0b7edc58e2d957e6ce18adf92c138b50dd78a420bebaf; + bytes32 + internal constant UPGRADE_INIT_TIMESTAMP_SLOT = 0x0816e8d9eeb2554df0d0b7edc58e2d957e6ce18adf92c138b50dd78a420bebaf; /// @notice Details of initialization data to be called on the second step /// of upgrade. diff --git a/solidity/contracts/BondedECDSAKeepVendorImplV1.sol b/solidity/contracts/BondedECDSAKeepVendorImplV1.sol index 98305a410..ff5477ab7 100644 --- a/solidity/contracts/BondedECDSAKeepVendorImplV1.sol +++ b/solidity/contracts/BondedECDSAKeepVendorImplV1.sol @@ -20,7 +20,6 @@ import "@keep-network/keep-core/contracts/KeepRegistry.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - /// @title Bonded ECDSA Keep Vendor /// @notice The contract is used to obtain a new Bonded ECDSA keep factory. contract BondedECDSAKeepVendorImplV1 is IBondedECDSAKeepVendor { diff --git a/solidity/contracts/CloneFactory.sol b/solidity/contracts/CloneFactory.sol index 210d9690f..ba6e43f58 100644 --- a/solidity/contracts/CloneFactory.sol +++ b/solidity/contracts/CloneFactory.sol @@ -1,6 +1,5 @@ pragma solidity 0.5.17; - /* The MIT License (MIT) Copyright (c) 2018 Murray Software, LLC. diff --git a/solidity/contracts/KeepBonding.sol b/solidity/contracts/KeepBonding.sol index bb26b2b3b..2a8543bed 100644 --- a/solidity/contracts/KeepBonding.sol +++ b/solidity/contracts/KeepBonding.sol @@ -19,7 +19,6 @@ import "./AbstractBonding.sol"; import "@keep-network/keep-core/contracts/TokenGrant.sol"; import "@keep-network/keep-core/contracts/libraries/RolesLookup.sol"; - /// @title Keep Bonding /// @notice Contract holding deposits from keeps' operators. contract KeepBonding is AbstractBonding { diff --git a/solidity/contracts/Migrations.sol b/solidity/contracts/Migrations.sol index 64c1e820d..74ecda2f0 100644 --- a/solidity/contracts/Migrations.sol +++ b/solidity/contracts/Migrations.sol @@ -1,6 +1,5 @@ pragma solidity >=0.4.21 <0.6.0; - contract Migrations { address public owner; uint256 public last_completed_migration; diff --git a/solidity/package-lock.json b/solidity/package-lock.json index a37955478..520d06a03 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -1943,6 +1943,12 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, + "@solidity-parser/parser": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-kUVUvrqttndeprLoXjI5arWHeiP3uh4XODAKbG+ZaWHCVQeelxCbnXBeWxZ2BPHdXgH0xR9dU1b916JhDhbgAA==", + "dev": true + }, "@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -2175,9 +2181,9 @@ } }, "acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", "dev": true }, "acorn-jsx": { @@ -3831,9 +3837,9 @@ } }, "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true }, "cliui": { @@ -4774,8 +4780,8 @@ "dev": true }, "eslint-config-keep": { - "version": "github:keep-network/eslint-config-keep#fed0f9a92d4bb4abbf738266d7521b56a08987ee", - "from": "github:keep-network/eslint-config-keep#0.3.0", + "version": "git+https://github.com/keep-network/eslint-config-keep.git#13a8031dc087f084cb28bd9ce20c7a4f956f8c89", + "from": "git+https://github.com/keep-network/eslint-config-keep.git", "dev": true, "requires": { "eslint-config-google": "^0.13.0", @@ -4785,9 +4791,9 @@ } }, "eslint-config-prettier": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz", - "integrity": "sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", + "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", "dev": true, "requires": { "get-stdin": "^6.0.0" @@ -4800,18 +4806,18 @@ "dev": true }, "eslint-plugin-prettier": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", - "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", + "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0" } }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4828,9 +4834,9 @@ } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true }, "espree": { @@ -4860,18 +4866,18 @@ } }, "esquery": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", - "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", "dev": true, "requires": { - "estraverse": "^5.0.0" + "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", - "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true } } @@ -5510,6 +5516,187 @@ "strip-hex-prefix": "1.0.0" } }, + "ethlint": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/ethlint/-/ethlint-1.2.5.tgz", + "integrity": "sha512-x2nKK98zmd72SFWL3Ul1S6scWYf5QqG221N6/mFNMO661g7ASvTRINGIWVvHzsvflW6y4tvgMSjnTN5RCTuZug==", + "dev": true, + "requires": { + "ajv": "^5.2.2", + "chokidar": "^1.6.0", + "colors": "^1.1.2", + "commander": "^2.9.0", + "diff": "^3.5.0", + "eol": "^0.9.1", + "js-string-escape": "^1.0.1", + "lodash": "^4.14.2", + "sol-digger": "0.0.2", + "sol-explore": "1.6.1", + "solium-plugin-security": "0.1.1", + "solparse": "2.2.8", + "text-table": "^0.2.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, "eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", @@ -6009,9 +6196,9 @@ } }, "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, "follow-redirects": { @@ -21521,21 +21708,21 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", + "chalk": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", + "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", - "rxjs": "^6.5.3", + "rxjs": "^6.6.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" @@ -21558,9 +21745,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -21589,9 +21776,9 @@ "dev": true }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "strip-ansi": { @@ -21847,12 +22034,6 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -23828,31 +24009,43 @@ } }, "prettier-plugin-solidity": { - "version": "1.0.0-alpha.47", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-alpha.47.tgz", - "integrity": "sha512-3cDt2SEJz0irrTpx9UE2CFKYQ1Z9eDfjVm5jatpYqZSkCueEBDaPKEA6rrBPfz6Z6jVbjNKoDuJDzHa/y7oleQ==", + "version": "1.0.0-alpha.55", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-alpha.55.tgz", + "integrity": "sha512-6B5wNRLu2iguuSz5POpBN9gRlaJPbmZjA/JGSjpwEQ4QPbY0/+fAmV1Qlrjj/xyDC37kx0khaTrQK46jH37+wg==", "dev": true, "requires": { + "@solidity-parser/parser": "^0.6.2", "dir-to-object": "^2.0.0", - "emoji-regex": "^8.0.0", - "escape-string-regexp": "^2.0.0", + "emoji-regex": "^9.0.0", + "escape-string-regexp": "^4.0.0", "extract-comments": "^1.1.0", - "prettier": "^2.0.2", - "semver": "^7.1.3", - "solidity-parser-diligence": "^0.4.18", + "prettier": "^2.0.5", + "semver": "^7.3.2", "string-width": "^4.2.0" }, "dependencies": { + "emoji-regex": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.0.0.tgz", + "integrity": "sha512-6p1NII1Vm62wni/VR/cUMauVQoxmLVb9csqQlvLz+hO2gk8U2UYDfXHQSUYIBKmZwAKz867IDqG7B+u0mj+M6w==", + "dev": true + }, "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true } } @@ -24391,13 +24584,10 @@ } }, "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true }, "rustbn.js": { "version": "0.2.0", @@ -24405,9 +24595,9 @@ "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", + "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -24917,196 +25107,9 @@ "resolved": "https://registry.npmjs.org/solidity-parser-antlr/-/solidity-parser-antlr-0.4.11.tgz", "integrity": "sha512-4jtxasNGmyC0midtjH/lTFPZYvTTUMy6agYcF+HoMnzW8+cqo3piFrINb4ZCzpPW+7tTVFCGa5ubP34zOzeuMg==" }, - "solidity-parser-diligence": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/solidity-parser-diligence/-/solidity-parser-diligence-0.4.18.tgz", - "integrity": "sha512-mauO/qG2v59W9sOn5TYV2dS7+fvFKqIHwiku+TH82e1Yca4H8s6EDG12ZpXO2cmgLlCKX3FOqqC73aYLB8WwNg==", - "dev": true - }, - "solium": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/solium/-/solium-1.2.5.tgz", - "integrity": "sha512-NuNrm7fp8JcDN/P+SAdM5TVa4wYDtwVtLY/rG4eBOZrC5qItsUhmQKR/YhjszaEW4c8tNUYhkhQcwOsS25znpw==", - "dev": true, - "requires": { - "ajv": "^5.2.2", - "chokidar": "^1.6.0", - "colors": "^1.1.2", - "commander": "^2.9.0", - "diff": "^3.5.0", - "eol": "^0.9.1", - "js-string-escape": "^1.0.1", - "lodash": "^4.14.2", - "sol-digger": "0.0.2", - "sol-explore": "1.6.1", - "solium-plugin-security": "0.1.1", - "solparse": "2.2.8", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, "solium-config-keep": { - "version": "github:keep-network/solium-config-keep#454a62e7e097350487ad28f7b0d895976b9e3d71", - "from": "github:keep-network/solium-config-keep#0.1.1", + "version": "github:keep-network/solium-config-keep#7ea21310ee14f12d65532ff7e3c79b0492d238ce", + "from": "github:keep-network/solium-config-keep#0.1.2", "dev": true }, "solium-plugin-security": { @@ -25455,9 +25458,9 @@ } }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { @@ -26051,9 +26054,9 @@ "dev": true }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "dev": true }, "tsort": { @@ -26303,9 +26306,9 @@ "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "dev": true }, "v8flags": { diff --git a/solidity/package.json b/solidity/package.json index 6cbe670b9..fe8a87b01 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -54,13 +54,13 @@ "bn-chai": "^1.0.1", "chai": "^4.2.0", "eslint": "^6.8.0", - "eslint-config-keep": "github:keep-network/eslint-config-keep#0.3.0", + "eslint-config-keep": "git+https://github.com/keep-network/eslint-config-keep.git", + "ethlint": "^1.2.5", "mocha": "^7.1.1", "prettier": "^2.0.2", "prettier-plugin-solidity": "^1.0.0-alpha.47", "solc": "0.5.17", - "solium": "^1.2.5", - "solium-config-keep": "github:keep-network/solium-config-keep#0.1.1", + "solium-config-keep": "github:keep-network/solium-config-keep#0.1.2", "toml": "^3.0.0", "tomlify-j0.4": "^3.0.0", "truffle": "^5.1.10", From d49f2b0a471735ede73048ac2272d365c48f07c1 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 12 Aug 2020 10:51:49 +0200 Subject: [PATCH 7/9] Changed abstract functions in bonding to public We will need to get to the functions from keep facotry and keeps. --- solidity/contracts/AbstractBonding.sol | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/solidity/contracts/AbstractBonding.sol b/solidity/contracts/AbstractBonding.sol index 4ebfdc52e..fbfd90c1e 100644 --- a/solidity/contracts/AbstractBonding.sol +++ b/solidity/contracts/AbstractBonding.sol @@ -314,25 +314,6 @@ contract AbstractBonding is IBonding { return authorizedPools[_operator][_poolAddress]; } - /// @notice Withdraws the provided amount from unbonded value of the - /// provided operator to operator's beneficiary. If there is no enough - /// unbonded value or the transfer failed, function fails. - function withdrawBond(uint256 amount, address operator) internal { - require( - unbondedValue[operator] >= amount, - "Insufficient unbonded value" - ); - - unbondedValue[operator] = unbondedValue[operator].sub(amount); - - address beneficiary = beneficiaryOf(operator); - - (bool success, ) = beneficiary.call.value(amount)(""); - require(success, "Transfer failed"); - - emit UnbondedValueWithdrawn(operator, beneficiary, amount); - } - /// @notice Checks if operator contract has been authorized for the provided /// operator. /// @param _operator Operator address. @@ -340,18 +321,37 @@ contract AbstractBonding is IBonding { function isAuthorizedForOperator( address _operator, address _operatorContract - ) internal view returns (bool); + ) public view returns (bool); /// @notice Gets the authorizer for the specified operator address. /// @param _operator Operator address. /// @return Authorizer address. - function authorizerOf(address _operator) internal view returns (address); + function authorizerOf(address _operator) public view returns (address); /// @notice Gets the beneficiary for the specified operator address. /// @param _operator Operator address. /// @return Beneficiary address. function beneficiaryOf(address _operator) - internal + public view returns (address payable); + + /// @notice Withdraws the provided amount from unbonded value of the + /// provided operator to operator's beneficiary. If there is no enough + /// unbonded value or the transfer failed, function fails. + function withdrawBond(uint256 amount, address operator) internal { + require( + unbondedValue[operator] >= amount, + "Insufficient unbonded value" + ); + + unbondedValue[operator] = unbondedValue[operator].sub(amount); + + address beneficiary = beneficiaryOf(operator); + + (bool success, ) = beneficiary.call.value(amount)(""); + require(success, "Transfer failed"); + + emit UnbondedValueWithdrawn(operator, beneficiary, amount); + } } From 87c75232b0cfb41f5d9611c1bbc49f4e44257c94 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 12 Aug 2020 10:56:48 +0200 Subject: [PATCH 8/9] Updated abstract bonding stub functions to public --- solidity/contracts/test/AbstractBondingStub.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solidity/contracts/test/AbstractBondingStub.sol b/solidity/contracts/test/AbstractBondingStub.sol index 69a5d39b6..7fd083881 100644 --- a/solidity/contracts/test/AbstractBondingStub.sol +++ b/solidity/contracts/test/AbstractBondingStub.sol @@ -25,7 +25,7 @@ contract AbstractBondingStub is AbstractBonding { function isAuthorizedForOperator( address _operator, address _operatorContract - ) internal view returns (bool) { + ) public view returns (bool) { return stakingInfoStub.isAuthorizedForOperator( _operator, @@ -33,12 +33,12 @@ contract AbstractBondingStub is AbstractBonding { ); } - function authorizerOf(address _operator) internal view returns (address) { + function authorizerOf(address _operator) public view returns (address) { return stakingInfoStub.authorizerOf(_operator); } function beneficiaryOf(address _operator) - internal + public view returns (address payable) { From ad6a1d7b4bd3a42ddfaf03534d539540c2f84040 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 12 Aug 2020 11:00:27 +0200 Subject: [PATCH 9/9] Updated functions visibility in KeepBonding --- solidity/contracts/KeepBonding.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solidity/contracts/KeepBonding.sol b/solidity/contracts/KeepBonding.sol index 2a8543bed..2042ef070 100644 --- a/solidity/contracts/KeepBonding.sol +++ b/solidity/contracts/KeepBonding.sol @@ -90,17 +90,17 @@ contract KeepBonding is AbstractBonding { function isAuthorizedForOperator( address _operator, address _operatorContract - ) internal view returns (bool) { + ) public view returns (bool) { return tokenStaking.isAuthorizedForOperator(_operator, _operatorContract); } - function authorizerOf(address _operator) internal view returns (address) { + function authorizerOf(address _operator) public view returns (address) { return tokenStaking.authorizerOf(_operator); } function beneficiaryOf(address _operator) - internal + public view returns (address payable) {