The Keeper is the one who initiates execution of a task.


The keeper is a combination of two blockchain addresses and an off-chain bot. The purpose of the bot is to watch all jobs available in the PowerAgent and offer its candidature for task execution as soon as the conditions for the execution are met.
Keeper is an externally owned account linked to an abstract Keeper object by a key-Worker (abstract object used in PowerAgent to represent keepers) relation; groups of Workers also may share Admins. The purpose of the keeper to invoke Agent's function execute_44g58pv when conditions are met.


A keeper needs two addresses: Admin address for management and Worker address for execution.
  • Admin - this address is used for management of CVP stake and registration and configuration of Worker addresses.
  • Worker - is registered as the keeper address in the Agent contract. On behalf of this address the tasks are executed. The rewards may be configured to be paid immediately to this address or be collected on the Agent contract and thereafter withdrawn, refer to <section>.


The bot connects to the blockchain via a websocket RPC connection and continuously listens to events from the blockchain. When the conditions for the execution are met, the bot sends the transaction from the Worker address to the Agent contract. It is recommended to set up a private node to maintain the quality of connection on one's own.
\cdot \cdot \cdot


This section describes the on-chain part of the Keeper only. For off-chain software, refer to the <section>.


The Keeper is represented in the Agent as the following structure Keeper:
Keeper structure
struct Keeper {
//address of the keeper
address worker;
//keeper’s stake in CVP
uint88 cvpStake;
//indicator of the keeper being active (i.e. able to execute jobs)
bool isActive;


All mappings related to the Keeper structure in the Agent:
Keeper mappings list
//Internally tracked ID (enumeration of keepers)
mapping(uint256 => Keeper) keepers;
//Keeper’s admin, which is defined as an address that is permitted to authorise changes to this keeper
mapping(uint256 => address) keeperAdmins;
//The totality of the CVP amount for which a keeper’s stake is slashed
mapping(uint256 => uint256) slashedStakeOf;
//The compensation in native chain tokens a keeper has accrued
mapping(uint256 => uint256) compensations;
//The amount of CVP for which the redemption process has started and is currently pending
mapping(uint256 => uint256) pendingWithdrawalAmounts;
//The timestamp at which the time-out ends, allowing the keeper or the admin thereof to call finalizeRedeem and withdraw the pending amount.
mapping(uint256 => uint256) pendingWithdrawalEndsAt;
//Reverse mapping of keepers, defining an isomorphism between keeper addresses and IDs.
mapping(address => uint256) workerKeeperIds;
RanDAO-specific mappings
// Gives a set of pending (locking in a certain sense) jobs for a keeper
mapping(uint256 => EnumerableSet.Bytes32Set) internal keeperLocksByJob;
// Gives timestamp at which keeper activation (staggered in time in RanDAO realisation) can be finalised
mapping(uint256 => uint256) public keeperActivationCanBeFinalizedAt;


The list of functions used to manage and update Keeper information:


Registers a new keeper and stores the information in the corresponding mappings.
function registerAsKeeper (
//keeper worker address
address worker_,
//initial CVP stake deposit amount
uint256 initialDepositAmount_
//returns id of the newly registered keeper
) public virtual returns (uint256 keeperId)
//asserts the provided address is not already a worker
//asserts the provided CVP meets the minimal amount requirement


Updates a keeper’s worker address. Should be called from keeper admin address.
function setWorkerAddress (
//id of the keeper which worker address should be changed
uint256 keeperId_,
//a new address for the worker
address worker_
) external
//Only Admin address is allowed to change the worker address
//Assert that this address is not already registered as a worker


Immediately withdraws the keeper’s compensation in native tokens.
functinon withdrawCompensation() (
//id of the keeper to withdraw compensation for
uint256 keeperId_,
//address to which to withdraw the compensation
address payable to_,
//the amount to withdraw
uint256 amount_
) external
//Assert that there is something to withdraw
//Only keeper admin and worker addresses are allowed to withdraw compensation
//Can't withdraw more compensation than is available


Deposits CVP to a given keeper’s stake.
function stake (
//id of the keeper to stake CVP for
uint256 keeperId_,
//amount of CVP to stake
uint256 amount_
) external
//Assert that the amount provided is not zero
//Assert that there is a keeper assigned to this id
//Assert that the final amount after stake doesn't overflow max amount (uint88)


Prepares CVP for withdrawal by first burning staked tokens to compensate the slashed amount (if any) and then accumulating the remainder as amount pending for withdrawal. The cooldown period, specified by the agent config, is re-initiated after every call of the function, even if the pending value before the call was nonzero.
function initiateRedeem (
//id of the keeper to reedem from
uint256 keeperId_,
//amount of CVP to redeem
uint256 amount_,
//Returns the timestamp when the redeem can be finalized
) external returns (uint256 pendingWithdrawalAfter)
//Only keeper admin
//Can't redeem nothing
//Hook before the Redeem, specifiable by particular realisations
//Can't redeem more than is staked
//If the keeper undergoes slashing, can't withdraw the part that is reserved for slashing until all slashed amount is compensated


Finalizes CVP withdrawal and sends the previously staked tokens to a specified address.
function finalizeRedeem (
//id of the keeper to finalize redeem for
uint256 keeperId_
//address to transfer the redeemed CVP to
address to_
//Returns the amount of CVP redeemed in case of success
) external returns (uint256 redeemedCvp)
//Assert that the withdrawal cooldown period has passed
//Assert that withdrawal of a nonzero amount is actually pending


Manually releases a keeper from executing a job.
function releaseJob(
//key of the job to release
bytes32 jobKey_
) external {
//only Admin can release
// Release if insufficient credits
if (_releaseKeeperIfRequired(jobKey_, assignedKeeperId)) {
// 2. Check interval timeouts otherwise
// 2.1 If interval job
uint256 period2EndsAt = lastExecutionAt + rdConfig.period1 + rdConfig.period2;
if (period2EndsAt > block.timestamp) {
revert TooEarlyToRelease(jobKey_, period2EndsAt);
} // else can release
// 2.2 If resolver job
// if slashing process initiated
if (_jobSlashingPossibleAfter != 0) {
//admissibility period end timestamp
uint256 period2EndsAt = _jobSlashingPossibleAfter + rdConfig.period2;
//can't release if locked by initiated slashing
if (period2EndsAt > block.timestamp) {
revert TooEarlyToRelease(jobKey_, period2EndsAt);
// if no slashing initiated
} else {
revert CantRelease();
_releaseKeeper(jobKey_, assignedKeeperId);