Keeper staking, withdrawal, and deactivation
Deactivation, or removal, of keepers is not described in the core contract and is left to inheritors to implement by using the provided keeper data structure (which conveniently lists activity)
Alongside with the initial stake, provided during keeper registration, the keeper can top up or redeem their stake and compensation at any time, provided certain conditions are fulfilled.
Keeper may also pause and continue their participation in the PowerAgent.
Increasing CVP stake may be needed for partaking in execution of those Jobs that require higher minimal CVP stake than default.
The specified amount of tokens is transferred from the caller to the Agent contract, and the keeper data stored in the mapping
keepersis updated (the stored cvpStake value is incremented by the added amount).
On successful staking an event is emitted:
//id of the keeper whose stake was incremented
uint256 indexed keeperId,
//amount of stake increment
//address of the user who staked
Since the native chain tokens (in which the keepers are rewarded by default) are not staked, no time delay is necessary to withdraw them. To withdraw the rewards, call
The amount of available compensation for this keeper, which is stored at its index in the mapping
compensations, is decremented by the withdrawn amount, and this amount of the native token is transferred to the specified address.
On successful withdrawal, an event is emitted:
event WithdrawCompensation (
//id of the keeper to withdraw compensation from
uint256 indexed keeperId,
//address to withdraw to
address indexed to,
//amount to withdraw
CVP stake of the keeper is used as an insurance against their malicious behavior. That is why the process of stake redemption is not instant and has a cooldown period, during which the stake is considered to be withdrawn, but is actually held on the Agent contract. By the end of the period the withdrawn CVP stake is transferred to the withdrawer.
The CVP withdrawal process is divided into two successive parts:
Implementation is dependent on realization of the PowerAgent by way of the overridable
When a keeper admin decides to withdraw some of their CVP stake, they call the function
initiateRedeem. This function checks that the keeper is not assigned to any oncoming job.
Firstly, it is important to understand the difference in slashing between Flashbots and RanDAO.
In Flashbots, the slashed amount is not simply deducted from the keeper's stake; instead, it is transferred to be stored in the
slashedStakeOfmapping, becoming inactive for the purposes of stake comparison and essentially representing the keeper's liability that must be burned before they can redeem any of their CVP, and a corresponding amount of tokens is sent to an Agent owner-specified address. The deduction is therefore finalised when the keeper wants to withdraw their stake (it is also possible to directly slash the pending withdrawals, in which case their value is decremented directly, and a corresponding amount of CVP is sent to the specified address).
If the amount declared for withdrawal is not in excess of the slashed stake totality, the redemption initiation reverts. Otherwise, the accrued slashed stake is zeroed (i.e. burned, since all tokens corresponding to this stake have been transferred to an Agent owner-specified address when slashing commenced), and the remaining amount is subtracted from the keeper's stake and added to the mapping
pendingWithdrawalAmounts. The timestamp of withdrawal finalization is stored in the mapping
Note that calling
initiateRedeemwhile the previous redeem was initiated will reset the period, and the redeemer will have to wait for full period again. The amount, however, will be the sum of both redeems.
Note also that if the keeper wishes to simply zero out their slashed stake, they need to call the
initiateRedeemfunction with exactly the totality of the slashed amount passed as the corresponding parameter. The slashed stake will be burned, finalising the accrued punishment, but the keeper will once more be made able to withdraw their stake.
Keeper A initially has his CVP stake at a value 100 and is in the process of withdrawing 50 CVP. Keeper A is slashed with values (
uint256 pendingAmount_) = (
20). The pending CVP is decremented by 20, A's
cvpStakevalue is set at 70, 20+30 = 50 tokens in total are transferred to the address specified by the Agent owner at call to
slash, and A's
slashedStakeOfvalue is set at 30. For the purposes of all stake amount checks, he now has 70 tokens for his stake. Though he is still pending withdrawal of 30 CVP (and can finalize it as usual!), if he desires to withdraw 10 more and calls
initiateRedeemwith the amount of 10, the function will revert because his slashed stake has a value of 30, and 10<30. Instead, to add 10 to his pending withdrawal, A must specify the value of 10+30 = 40. If he does so, his slashed stake will be zeroed out (effectively burned, but remember - the corresponding CVP tokens have not disappeared; they have been long since transferred to the address specified by the Agent owner in his call to
slash), and 40-30 = 10 will be added to his pending value.
Shares the implementation with the exception that in the
_beforeInitiateRedeemhook, it is checked whether the keeper has jobs pending. A keeper who has jobs pending is prohibited from initiating redemption as a measure of preventing malicious behaviour (e.g. withdrawing the totality of stake whilst assigned to a job and both failing execution and dodging punishment).
event InitiateRedeem (
//id of the keeper which stake is being redeemed
uint256 indexed keeperId,
//amount to redeem
//current keeper stake
//keeper's slashed stake
Doesn't depend on PowerAgent realization.
Once the timeout period elapses, the keeper admin may call
finalizeRedeemto finalise the CVP withdrawal.
The entire pending withdrawal amount is transferred to the supplied address, and the value in the corresponding mapping
event FinalizeRedeem (
//id of the keeper which stake has been redeemed
uint256 indexed keeperId,
//address of the redeemed stake recepient
address indexed beneficiary,
Activation and deactivation of keepers means adding or removing keepers to/from the pool of active keepers. To be eligible for execution and slashing duties, a keeper has to be in this pool. The keeper might want to suspend the participation: for this the methods of activation and deactivation are implemented.
Much like the CVP token withdrawal, the keeper activation is a process staggered in time. This is done to curb the possibility of the active keeper pool oscillating violently.
When the admin of an inactive keeper deems it fit to reactivate the keeper once again, he invokes the function
initiateKeeperActivation. If the keeepr is not already active, a timeout is imposed, the duration of which is set by the Agent parameter
keeperActivationTimeoutHours(max 255 hours), and the time after which finalisation will be made possible is stored in the mapping
keeperActivationCanBeFinalizedAtwith the keeper ID as key.
On initiating activation emits
//ID of the keeper being activated
//timestamp at which activation may be finalised
Finalisation of activation is done by the keeper admin invoking
finalizeKeeperActivation. If the function is called at a timestamp not earlier than the one specified at
keeperActivationCanBeFinalizedAt, the ID of the keeper being activated is added back to the set of active keepers, and the activity status of a corresponding entry in the mapping of IDs to keepers is set to
On finalising activation emits
//ID of the keeper now active
Deactivation does not require any complicated multi-step procedure; a simple call to
disableKeeperby the keeper admin suffices. If the keeper in question is not already inactive and can be released (i.e. has no jobs pending), they are removed from the active keeper set, and the corresponding entry in the mapping of IDs to keepers has its activity status set to
On keeper deactivation emits
//the ID of the deactivated keeper