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)
Overview
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.
â â â
Specification
Staking
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 mappingkeepers is updated (the stored cvpStake value is incremented by the added amount).
On successful staking an event is emitted:
eventStake( //id of the keeper whose stake was incrementeduint256indexed keeperId, //amount of stake incrementuint256 amount, //address of the user who stakedaddress staker)
Native token compensation withdrawal
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 withdrawCompensation.
The amount of available compensation for this keeper, which is stored at its index in the mappingcompensations, 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:
eventWithdrawCompensation ( //id of the keeper to withdraw compensation fromuint256indexed keeperId, //address to withdraw toaddressindexed to, //amount to withdrawuint256 amount)
CVP stake redemption
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: initiation and finalization.
Initiation
Implementation is dependent on realization of the PowerAgent by way of the overridable virtual function _beforeInitiateRedeem.
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.
Flashbots
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 slashedStakeOf mapping, 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 pendingWithdrawalEndsAt.
Note that calling initiateRedeem while 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 initiateRedeem function 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.
Simple example of stake slashing in Flashbots
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 currentAmount_, uint256 pendingAmount_) = (30, 20). The pending CVP is decremented by 20, A's cvpStake value 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 slashedStakeOf value 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 initiateRedeem with 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.
RanDAO
Shares the implementation with the exception that in the _beforeInitiateRedeem hook, 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).
Emits
eventInitiateRedeem ( //id of the keeper which stake is being redeemeduint256indexed keeperId, //amount to redeemuint256 redeemAmount, //current keeper stakeuint256 stakeAmount, //keeper's slashed stakeuint256 slashedStakeAmount)
Finalization
Doesn't depend on PowerAgent realization.
Once the timeout period elapses, the keeper admin may call finalizeRedeem to finalise the CVP withdrawal.
The entire pending withdrawal amount is transferred to the supplied address, and the value in the corresponding mapping pendingWithdrawalEndsAt is zeroed.
Emits
eventFinalizeRedeem ( //id of the keeper which stake has been redeemeduint256indexed keeperId, //address of the redeemed stake recepientaddressindexed beneficiary, //aomunt redeemeduint256 amount)
Activation and deactivation
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.
Flashbots
Not implemented; to suspend activity, simply stop the off-chain keeper software. To continue, run it again.
RanDAO
Activation
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.
Initiation
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 keeperActivationCanBeFinalizedAt with the keeper ID as key.
Event
On initiating activation emits
eventInitiateKeeperActivation(//ID of the keeper being activateduint256 keeperId, //timestamp at which activation may be finaliseduint256 canBeFinalizedAt);
Finalisation
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 True.
Event
On finalising activation emits
eventFinalizeKeeperActivation(//ID of the keeper now activeuint256 keeperId);
Deactivation
Deactivation does not require any complicated multi-step procedure; a simple call to disableKeeper by 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 False.
Event
On keeper deactivation emits
eventDisableKeeper(//the ID of the deactivated keeperuint256 keeperId);