This repository has been archived by the owner on May 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
2 changed files
with
353 additions
and
318 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
Oops, something went wrong.