A node responsible for task execution


A PowerAgent node (Keeper) is a bot that monitors and executes tasks submitted into the PowerAgent network. It is a lightweight software intended to be run 24/7.

The keeper is registered in the Agent contract. A keeper has an admin address (typically this would be your main address) and a worker address (the address from which the keeper bot will send transactions). Multiple workers may be assigned to a single admin address.

I want to launch my own PowerAgent note

Refer to this section.



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 - this address is registered as the keeper address in the Agent contract. On behalf of this address the tasks are executed. This address receives rewards for execution. The rewards may be paid immediately to this address or collected later on the Agent contract (from the Admin account). Read this section for more details.

You can have several keepers share the admin address, but the worker address is unique for each keeper.

While you can have the same admin and worker addresses, this is highly not recommended because it can potentially cause race conditions with respect to the nonce generation.

The same holds for an address used for anything else than signing automated transactions (e.g. a validator address).

Please, use a dedicated address as a worker address.


Node (bot)

The bot is a program that connects to the blockchain and continuously listens to the events from the Agent contract. It queries from the Agent and then stores information about all active jobs.

For interval jobs, the bot sets the corresponding timers. Once the timer goes off, the bot will check whether its worker EOA is a chosen executor. If so, it will send the execution transaction from this EOA. If not, nothing will happen.

For resolver jobs, the bot statically (off-chain) executes the resolver function every block. When the function returns a tuple (true, calldata), the bot will send an execution transaction passing the calldata acquired from the resolver.

By default, before the execution the Agent checks the resolver to confirm the calldata. However, if the resolver is very computationally heavy to be performed on-chain, the check will be skipped. Keep in mind the degradation of security associated with the usage of such resolvers.

Stake management, slashing, and you

Stake management

A keeper must possess stake - some amount of CVP locked in the contract (e.g., currently it is set at a level of 1000 CVP) - in order to become eligible to execute jobs.

Some jobs may impose higher CVP thresholds due to the higher potential loss associated with the failure of execution, and the stake amount accordingly increases the rewards gained by the keeper for each execution (there is a configurable upper bound on the stake for the purpose of compensation computation).

The gas compensation is paid out in all cases of transaction execution, whereas the reward is paid out only in case of a successful job execution (i.e. the tx was not reverted).

The formula for the reward reads:

C=F+b⋅(gu+go)⋅M+min(s,js)DC = F + b\cdot(g_u + g_o) \cdot M + \frac{min(s, j_s)}{D}


  • FF - fixed reward (const)

  • bb - base fee (const)

  • gug_u - gas used during execution

  • gog_o - gas overhead (const)

  • MM - reward multiplier (const)

  • ss - keeper's stake

  • jsj_s - job-specific maximal stake

  • DD - stake divisor (const)

In the future, we plan to have the stake impact one’s random selection weights to incentivize staking.

The purpose of demanding staking is to disincentivize malicious behavior; failure to execute the job on time will lead to slashing (partial loss of stake).


Slashing works as follows:

  • Once execution is available, the assigned keeper has a certain grace period to execute the job.

  • Afterwards, he becomes fair game for the assigned slasher.

  • If the slasher executes the job first, he gains the reward and slashes the negligent keeper’s stake.

  • The amount slashed depends on the slashed keeper’s stake.

To prevent keeper activity manipulation (such as unstaking or deactivation before executing a certain job) protective measures are implemented:

  • CVP stake withdrawal has a 1-hour cooldown.

  • It is impossible to initiate CVP stake withdrawal if a keeper is assigned to any job.

  • If a Keeper is deactivated, it is not unassigned from the jobs it has been already assigned to (and can be slashed).

  • The activation also has a cooldown period. If you deactivate a keeper which has jobs assigned, there is a risk that your keeper will miss the execution and will get slashed.

  • A keeper will never be slashed all the way to zero under any circumstances, as it is automatically deactivated whenever his stake dips below the minimum.

How to become a keeper?

To join the PowerAgent network and become a keeper, you will need to register in the PowerAgent contract, stake at least 1000 CVP and run the PowerAgent software.

Refer to the dedicated page for step-by-step instructions!

Short specification

If you want to dive a little bit deeper into the details of the Keeper structure, read this section.

A registered keeper is represented inside the Agent contract as a struct with the following format:

	"admin": address, //Keeper's admin address that is whitelisted to govern it
	"worker": address immutable, //Keeper's worker address that is responsible
															 //for sending txns.
															 //The worker address is immutable once it is set
	"isActive": bool, //whether the keeper is active and can be assigned to jobs
	"currentStake": uint256, //the keeper's current active stake
													 //(i.e., not including pending withdrawals)
	"slashedStake": uint256, //meaningless for the RanDAO realisation; 
													 //a relic of the past when slashing was manual
	"compensation": uint256, //the amount of native tokens the keeper has accrued
													 //thus far as compensation for executing tasks
	"pendingWithdrawalAmount": uint256, //the stake amount pending withdrawal.
																			//Considered inactive and does not
																			//contribute to stake-dependent computations
	"pendingWithdrawalEndAt": uint256, //the timestamp at which one can finalise withdrawal
	"activationCanBeFinalizedAt": uint256, //the timestamp at which the keeper can finish
																				 //its activation
	"jobsAssignedToKeeper": bytes32[], //keys of jobs assigned to the keeper
	"id": uint256 immutable, //keeper's ordinal number

Note that the worker and id are immutable; provisions for changing the admin address are made in the code.

Of the above fields, you only explicitly specify the initial stake and the admin/worker address pair.

Last updated