Keeper (Old)

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

Overview

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.

Addresses

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>.

Bot

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.

Specification

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

Structure

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;

}

Mappings

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;  

Functions

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

registerAsKeeper

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
        _assertWorkerNotAssigned()
        //asserts the provided CVP meets the minimal amount requirement
        minKeeperCvp_assertion()
        ...
}

setWorkerAddress

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
        _assertOnlyKeeperAdmin()
        //Assert that this address is not already registered as a worker
        _assertWorkerNotAssigned()
        ...
}

withdrawCompensation

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
        _assertNonZeroAmount()
        //Only keeper admin and worker addresses are allowed to withdraw compensation
        _assertOnlyKeeperAdminOrWorker()
        //Can't withdraw more compensation than is available
        amount_not_exceeds_available_assertion()
        ...
}

stake

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
        _assertNonZeroAmount()
        //Assert that there is a keeper assigned to this id
        _assertKeeperIdExists()
        //Assert that the final amount after stake doesn't overflow max amount (uint88)
        no_amount_after_overflow_assertion()
        ...
}

initiateRedeem

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 
        _assertOnlyKeeperAdmin()
        //Can't redeem nothing
        _assertNonZeroAmount()
        //Hook before the Redeem, specifiable by particular realisations
        _beforeInitiateRedeem()
        //Can't redeem more than is staked
        amount_not_exceeds_stake_assertion()
        //If the keeper undergoes slashing, can't withdraw the part that is reserved for slashing until all slashed amount is compensated
        amount_sufficient_to_compensate_slashed_stake_assertion()
        ...
}

finalizeRedeem

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
        withdrawal_time_limit_elapsed_assertion()
        //Assert that withdrawal of a nonzero amount is actually pending
        pending_withdrawal_extant()
        ...
}

releaseJob

Manually releases a keeper from executing a job.

function releaseJob(
//key of the job to release
bytes32 jobKey_
) external {
  //only Admin can release
  _assertOnlyKeeperAdmin(assignedKeeperId);
  
  // Release if insufficient credits
  if (_releaseKeeperIfRequired(jobKey_, assignedKeeperId)) {
    return;
  }
  
  // 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);
}

Last updated