The Agent is the key smart contract in the PowerAgent network


The PPAgentV2 (later The Agent) contract stores the state of the PowerAgent network. It includes the list of keepers, the logic of task execution, and keeper management. PPAgentV2 is an upgradeable contract inherting from PPAgentV2.sol or any descendant thereof.
The Agent is responsible for the following operations:
  • Keeper Registration
    To participate in the PowerAgent network, a keeper has to register in the system by calling the registerAsKeeper function and provide at least minimal CVP stake.
  • Job Registration
    The contract to be automated must be registered in The Agent. For this, the owner of the contract provides the address of the contract, and the configuration of the job, including the type, and tops up the job balance for keepers' reward.
  • Transaction xecution
    To execute the task (once the conditions for execution are met), the keeper calls execute the Agent contract function. After a series of check-ups, the call is passed by The Agent to the Job contract and the task is executed, or in case of any errors, the execution will revert with a corresponding error.
  • Job Update
    Job owners can update the Job configuration at any time, for example, change the function of the contract to be executed, or update the conditions by which the contract should be executed. Each time a change is made, a Job with new id is created and attached to the same Job contract.
  • Slashing
    The PowerAgent has the algorithm of punishment for malicious or negligent keepers. Various tasks can have different cost of no execution, and expensive tasks require stable and reliable execution. To prevent keepers from misbehavior, the slashing system is implemented in The Agent. If the execution of a task is failed (or not performed in time), the keeper is slashed for a certain portion of their CVP stake. This portion depends on the task specific minimal CVP stake and the cost of no execution.
  • Keeper Removal
    Once a keeper wishes to quit participation in the PowerAgent network, they call the function initiateRedeem. A process of stake release is launched. After a cooldown period that is specified by the job parameters, the keeper can withdraw their CVP stake by invoking finalizeRedeem.
\cdot \cdot \cdot


Internal constants

The Agent possesses the following internal (i.e., accessible to inheritors) constants:
//Capping of the maximum possible delay in fund withdrawal
uint256 MAX_PENDING_WITHDRAWAL_TIMEOUT_SECONDS = 30 days; //2_592_000 sec
//Capping the maximum fee in parts per million an Agent can set
uint256 MAX_FEE_PPM = 5e4;
//Multiplier for fixed (i.e. static) rewards
Values of any inheritor Agent instance may not exceed the cap imposed by the corresponding internal parameter constant.

Internal variables

The Agent possesses the following internal (i.e., accessible to inheritors) variables:
//Minimal acceptable CVP value for the keeper stake to have
uint256 minKeeperCvp;
//Minimal time interval between redeem initialization and redeem finalization
uint256 pendingWithdrawalTimeoutSeconds;
//The totality of fees the Agent has accrued thus far
uint256 feeTotal;
//Fee retained by the Agent for its services in parts per million
uint256 feePpm;
//The index of last keeper (having the meaning of the keeper pool size)
uint256 lastKeeperId;
RanDAO realisation also possesses a configuration Struct rdConfig, constructed to be exactly 24 bytes in size.
struct RandaoConfig {
// max: 2^8 - 1 = 255 blocks
//the size of slashing epoch in blocks; may not be less than 3
uint8 slashingEpochBlocks;
// max: 2^24 - 1 = 16777215 seconds ~ 194 days
//the size of the grace period during which execution is possible, but the keeper is not slashable
//no less than 15 seconds
uint24 period1;
// max: 2^16 - 1 = 65535 seconds ~ 18 hours
//the size of the admissibility period, during which slashing is possible and cannot be re-initiated (i.e. execution is overdue, but the job does not yet have an overdue status). In addition, a job cannot be released by a keeper until overdue (this restriction does not apply to release by owner).
//no less than 15 seconds
uint16 period2;
// in 1 CVP. max: 16_777_215 CVP. The value here is multiplied by 1e18 in calculations.
//Fixed part of the slashing fee; no greater than half of the minimal Agent-wide admissible stake
uint24 slashingFeeFixedCVP;
// In BPS; 1 basis point = 0.01%
//governs the dynamic part of the slashing fee; no greater than 50% (5000 bps)
uint16 slashingFeeBps;
// max: 2^16 - 1 = 65535, in calculations is multiplied by 0.001 ether (1 finney),
// thus the min is 0.001 ether and max is 65.535 ether
//minimal amount of credits a job must possess to be assigned and retain keepers for execution; expressed in finneys (1 F = 1e-3 ETH)
uint16 jobMinCreditsFinney;
// max 2^40 ~= 1.1e12, in calculations is multiplied by 1 ether
// Agent-wide upper cap on the stake of a keeper for the purposes of computing compensation; set at zero to disable Agent-wide capping considerations (job-wide capping may stil be carried out in such a case)
//!!!WE MUST ASK KOLYAN WHETHER IT IS PLANNED TO CAP JOB WIDE MAX STAKE AT AGENT WIDE MAX STAKE; AS OF NOW, JOB WIDE MAX STAKE IS IMPOSSIBLE TO SET IN ANY WAY - ISSUE RESOLVED; job wide max stake is the fixedReward field in the binary representation. DELETE this comment when acknowledged !!!
uint40 agentMaxCvpStake;
// max: 2^16 - 1 = 65535, where 10_000 is 100%
// determines the size of the dynamic part of the compensation in basis points of total gas expenditure
uint16 jobCompensationMultiplierBps;
// max: 2^32 - 1 = 4_294_967_295
// determines the constant scaling factor of the (possible capped) keeper stake which yields the fixed part of the reward
uint32 stakeDivisor;
// max: 2^8 - 1 = 255 hours, or ~10.5 days
// determines the time delay between keeper activation being initiated and finalised
uint8 keeperActivationTimeoutHours;



The Agent instance is initialized by an eponymous function initialize which accepts the following arguments:
function initialize(
//Agent’s owner address
address owner;
//Minimal acceptable CVP value for the keeper stake to have
uint256 minKeeperCvp_;
//Minimal time interval between redeem initialization and redeem finalization
uint256 pendingWithdrawalTimeoutSeconds_;
This function sets the Agent’s configuration parameters and ownership to those specified.
In the RanDAO realisation, agent instantiation accepts additional arguments that are required for this implementation to work
function initializeRandao(
//Agent owner's address
address owner_,
//Minimal stake accepted for a keeper by the Agent
uint256 minKeeperCvp_,
//Minimal time interval between redeem initialization and redeem finalization
uint256 pendingWithdrawalTimeoutSeconds_,
//rdConfig parameters
RandaoConfig memory rdConfig_)
A detailed description of RandaoConfig is available at rdConfig.

Parameter update

Function setAgentParams updates the configuration of the Agent. Appropriate parameters are capped by the corresponding internal constants.
function setAgentParams(
//Minimal acceptable CVP for the keeper to stake
uint256 minKeeperCvp_,
//The time a stake withdrawal must spend in a pending state
uint256 timeoutSeconds_,
//Fee retained by the Agent in parts per million
uint256 feePpm_
In RanDAO, additional parameters are extant, calling for an extra parameter-setting function (setAgentParams is not virtual).
function _setRdConfig(RandaoConfig memory rdConfig_)
Since some of the capping constants are not defined in code, we supply the entire annotated checklist.
//make sure the slashing epoch is not too short; this is not a vitally necessary restriction, but it is useful, since it somewhat mitigates the effects of the grace period being no less than 15 seconds, giving keepers more time to execute slashing of interval jobs
if (rdConfig_.slashingEpochBlocks < 3) {
revert SlashingEpochBlocksTooLow();
//make sure the grace period is at least 15 seconds. Utility is quite self-explanatory, since it would be a bad move to permit slashing of keepers e.g. immediately when job execution is possible.
if (rdConfig_.period1 < 15 seconds) {
revert InvalidPeriod1();
//make sure the admissibility period is at least 15 seconds. Also self-explanatory; equivalent to period1, but for slashers.
if (rdConfig_.period2 < 15 seconds) {
revert InvalidPeriod2();
//make sure the fixed slashing fee is no greater than half of the minimum Agent-permitted stake. The one-half value is due to simultaneous dynamic slashing fee bps restriction.
if (rdConfig_.slashingFeeFixedCVP > (minKeeperCvp / 2)) {
revert InvalidSlashingFeeFixedCVP();
//make sure the dynamic slashing fee is no greater than 5000 bps/50%. Ensures that the slashed stake during any one slashing event is capped by 100% of the keeper's stake.
if (rdConfig_.slashingFeeBps > 5000) {
revert SlashingBpsGt5000Bps();
//make sure the inverse of the coefficient for the fixed (gas-independent) part of keeper reward is not zero
if (rdConfig_.stakeDivisor == 0) {
revert InvalidStakeDivisor();

Fee withdrawal

The function withdrawFees withdraws all fees the Agent has accrued to a specified address.
function withdrawFees(
//where to withdraw; needs to be payable to invoke transfer
address payable to_
) external {
//only Agent owner can call
//current accrued fees
uint256 amount = feeTotal;
//we withdraw all fees
feeTotal = 0;
//and transfer them to the specfieid address
emit WithdrawFees(to_, amount);

Custom structures

The Agent contract contains custom data structures storing information about Keepers and Jobs.
Refer to the Keeper and Job pages for detailed description.