Modifiers
Herein modifiers are described, including those that are not wrapped into their own distinct functions, instead being implemented as internal conditionals at execution.
A few times the function
ConfigFlags.check
is invoked to check whether a certain flag is true. This is done by storing a predefined library of possible flags and taking bitwise AND between the given flag and the provided config byte. library ConfigFlags {
function check(uint256 cfg, uint256 flag) internal pure returns (bool) {
return (cfg & flag) != 0;
}
}
Modifiers herein listed are wrapped in distinct functions.
This is a modifier on the agent ownership (incorporated via inheritance from the well-known Ownable.sol) which is used to keep unauthorized users from performing important function calls (slashing, agent parameter modification, accumulated fee withdrawal).
function _assertOnlyOwner() internal view {
if (msg.sender != owner()) {
revert OnlyOwner();
}
}
function slash(uint256 keeperId_, address to_, uint256 currentAmount_, uint256 pendingAmount_) external
function withdrawFees(address payable to_) external
function setAgentParams(uint256 minKeeperCvp_, uint256 timeoutSeconds_, uint256 feePpm_) external
This is a modifier on job ownership (incorporated via a custom jobkey-owner mapping) used to prevent unauthorized users from changing the settings of the job or withdrawing credits from its balance. Mind that they are still allowed to deposit credits therein, though).
function _assertOnlyJobOwner(bytes32 jobKey_) internal view {
if (msg.sender != jobOwners[jobKey_]) {
revert OnlyJobOwner();
}
}
function updateJob(bytes32 jobKey_, uint16 maxBaseFeeGwei_, uint16 rewardPct_, uint32 fixedReward_, uint256 jobMinCvp_, uint24 intervalSeconds_) external
function setJobResolver(bytes32 jobKey_, Resolver calldata resolver_) external
function setJobPreDefinedCalldata(bytes32 jobKey_, bytes calldata preDefinedCalldata_) external
function setJobConfig(bytes32 jobKey_, bool isActive_, bool useJobOwnerCredits_, bool assertResolverSelector_) public virtual
function initiateJobTransfer(bytes32 jobKey_, address to_) external
function withdrawJobCredits(bytes32 jobKey_, address payable to_, uint256 amount_) external
This is a modifier that checks whether the calling user is an Admin of the supplied keeper (incorporated via a custom keeperId-admin mapping) in order to prevent unauthorized users to change the parameters of a keeper or withdraw some of the CVP stake. Mind that withdrawal of native-token compensation uses a laxer version of this modifier, described in the next subsection.
function _assertOnlyKeeperAdmin(uint256 keeperId_) internal view {
if (msg.sender != keeperAdmins[keeperId_]) {
revert OnlyKeeperAdmin();
}
}
function setWorkerAddress(uint256 keeperId_, address worker_) external
function initiateRedeem(uint256 keeperId_, uint256 amount_) external returns (uint256 pendingWithdrawalAfter)
function finalizeRedeem(uint256 keeperId_, address to_) external returns (uint256 redeemedCvp)
This is a modifier that checks whether the calling user is an Admin or Worker of the supplied keepers (incorporated via a custom keeperId-admin mapping and Keeper data structure, respectively; in both cases an object,
address
or Keeper struct, is associated via some mapping with the keeperId), being a laxer version of the preceding one. In the base contract, it has the only use of providing pre-compensation withdrawal checks. function _assertOnlyKeeperAdminOrWorker(uint256 keeperId_) internal view {
if (msg.sender != keeperAdmins[keeperId_] && msg.sender != keepers[keeperId_].worker) {
revert OnlyKeeperAdminOrWorker();
}
}
function withdrawCompensation(uint256 keeperId_, address payable to_, uint256 amount_) external
This checks whether a keeper with a given ID exists at all by comparing the ID supplied with the ID of the latest registered keeper, since keepers are enumerated in order. Used to prevent staking to a non-existent keeper.
function stake(uint256 keeperId_, uint256 amount_) external
This checks that a worker with a given address is not already extant. Used to prevent setting two different keepers to have the same address.
function registerAsKeeper(address worker_, uint256 initialDepositAmount_) public virtual returns (uint256 keeperId)
function setWorkerAddress(uint256 keeperId_, address worker_) external
Asserts that the amount supplied is not zero. Used to cut off cases of attempts to invoke the deposition or withdrawal operator in a trivial setting.
function withdrawJobCredits(bytes32 jobKey_, address payable to_, uint256 amount_) external
function withdrawJobOwnerCredits(address payable to_, uint256 amount_) external
function stake(uint256 keeperId_, uint256 amount_) external
function initiateRedeem(uint256 keeperId_, uint256 amount_) external returns (uint256 pendingWithdrawalAfter)
function slash(uint256 keeperId_, address to_, uint256 currentAmount_, uint256 pendingAmount_) external
Similar to the preceding assertion, but for functions with the
payable
property instead (i.e. differs in that the amount is not passed explicitly, but rather inferred from the message value). Used to perform the same check for payable functions (e.g. credit deposition in native tokens). function depositJobCredits(bytes32 jobKey_) external virtual payable
function depositJobOwnerCredits(address for_) external payable
Asserts that the calldatasource field of the provided (by key) job is in concord with the specified source. Used to check that a job for which a type-specific property is set actually belongs to that type.
function setJobResolver(bytes32 jobKey_, Resolver calldata resolver_) external
function setJobPreDefinedCalldata(bytes32 jobKey_, bytes calldata preDefinedCalldata_) external
Asserts that the provided
maxBaseFeeGwei_
is nonzero, and that the fixed and dynamic parts of the reward (given correspondingly by the parameters fixed_reward_
and rewardPcts_
) is not zero, i.e., the components thereof are not zero simultaneously. function registerJob(RegisterJobParams calldata params_, Resolver calldata resolver_, bytes calldata preDefinedCalldata_) public payable virtual returns (bytes32 jobKey, uint256 jobId)
function updateJob(bytes32 jobKey_, uint16 maxBaseFeeGwei_, uint16 rewardPct_, uint32 fixedReward_, uint256 jobMinCvp_, uint24 intervalSeconds_) external
Asserts that the provided (by key) job is either of
RESOLVER
type AND does not have intervals or not of RESOLVER
type AND has nonzero interval
size specified. function registerJob(RegisterJobParams calldata params_, Resolver calldata resolver_, bytes calldata preDefinedCalldata_) public payable virtual returns (bytes32 jobKey, uint256 jobId)
function updateJob(bytes32 jobKey_, uint16 maxBaseFeeGwei_, uint16 rewardPct_, uint32 fixedReward_, uint256 jobMinCvp_, uint24 intervalSeconds_) external
Modifiers listed hereafter are not wrapped in distinct functions of their own, being instead implemented as ad-hoc logic inside of the functions that utilise them, and are listed under the provisional names given by us.
Asserts that the user other than a pending owner can not accept the job transfer.
if (msg.sender != jobPendingTransfers[jobKey_]) {
revert OnlyPendingOwner();
}
function acceptJobTransfer(bytes32 jobKey_, address to_) external
Asserts that after the fee (specified by the Agent parameter
feePpm
) has been deducted, the final balance after depositing additional tokens does not overflow the type used to store it (uint88
).//return the fee in accordance with the Agent parameter and the amount remaining after deduction thereof
(uint256 fee, uint256 amount) = _calculateDepositFee();
uint256 creditsAfter = jobs[jobKey_].credits + amount;
if (creditsAfter > type(uint88).max) {
revert CreditsDepositOverflow();
}
function _processJobCreditsDeposit(bytes32 jobKey_) internal
Asserts that for the newly registered job, the initial token balance does not overflow the type used to store it (
uint88
).//revert if the proposed job credit amount is not storable in uint88
if (msg.value > type(uint88).max) {
revert CreditsDepositOverflow();
}
function registerJob( RegisterJobParams calldata params_, Resolver calldata resolver_, bytes calldata preDefinedCalldata_ ) public payable virtual
Asserts that after the ID of a newly registered job does not overflow the
uint24
variable used to store it. Employed at registration, though it is unlikely that any owner ever registers type(uint24).max
jobs. if (jobId > type(uint24).max) {
revert JobIdOverflow();
}
function registerJob(
RegisterJobParams calldata params_,
Resolver calldata resolver_,
bytes calldata preDefinedCalldata_
) public payable virtual returns (bytes32 jobKey, uint256 jobId)
​
Asserts that the initial job credit deposit does not overflow the uint88 value used to store it. Registration analogue of other deposit checkers.
if (jobId > type(uint24).max) {
revert JobIdOverflow();
}
function registerJob(
RegisterJobParams calldata params_,
Resolver calldata resolver_,
bytes calldata preDefinedCalldata_
) public payable virtual returns (bytes32 jobKey, uint256 jobId)
Asserts that the job address provided in the registration parameters is extant and nonzero.
if (params_.jobAddress == address(0)) {
revert MissingJobAddress();
}
function registerJob(
RegisterJobParams calldata params_,
Resolver calldata resolver_,
bytes calldata preDefinedCalldata_
) public payable virtual returns (bytes32 jobKey, uint256 jobId)
Asserts that the specified calldata source is within the [0;2] range, since they are encoded by the first three nonnegative integers (SELECTOR, PRE_DEFINED, RESOLVER in that exact order).
if (params_.calldataSource > 2) {
revert InvalidCalldataSource();
}
function registerJob(
RegisterJobParams calldata params_,
Resolver calldata resolver_,
bytes calldata preDefinedCalldata_
) public payable virtual returns (bytes32 jobKey, uint256 jobId)
Asserts that the specified job address does not correspond to the CVP token or the Agent instance in order to prevent possible arbuses.
if (params_.jobAddress == address(CVP) || params_.jobAddress == address(this)) {
revert InvalidJobAddress();
}
function registerJob(
RegisterJobParams calldata params_,
Resolver calldata resolver_,
bytes calldata preDefinedCalldata_
) public payable virtual returns (bytes32 jobKey, uint256 jobId)
Asserts that the message sender that invokes
execute_44g58pv
is the keeper authorised to do so (checked via bytes reserved for technical variables in execute
calldata). Enforces compliance with the selection made. assembly ("memory-safe") {
// load jobAddress, cfg, and keeperId from calldata to the stack
actualKeeperId := shr(232, calldataload(28))
}
Keeper memory keeper = keepers[actualKeeperId];
if (keeper.worker != msg.sender) {
revert KeeperWorkerNotAuthorized();
}
function execute_44g58pv() external
Asserts that the message sender that invokes
execute_44g58pv
has at least the minimal stake demanded by the global level set by the Agent. Ensures a keeper has stake to slash even if the job does not specify any demands in this regard. assembly ("memory-safe") {
// load jobAddress, cfg, and keeperId from calldata to the stack
actualKeeperId := shr(232, calldataload(28))
}
Keeper memory keeper = keepers[actualKeeperId];
if (keeper.cvpStake < minKeeperCvp) {
revert InsufficientKeeperStake();
}
function execute_44g58pv() external
Asserts that the invoked job is active and therefore potentially executable.
assembly ("memory-safe") {
// size of (address(bytes20)+id(uint24/bytes3))
let size := 23
// keccack256(address+id(uint24)) to memory to generate jobKey
calldatacopy(0, 4, size)
jobKey := keccak256(0, size)
}
uint256 binJob = getJobRaw(jobKey);
if (!ConfigFlags.check(binJob, CFG_ACTIVE)) {
revert InactiveJob(jobKey);
function execute_44g58pv() external
Asserts that the keeper invoking
execute_44g58pv
has sufficient amount of stake with respect to the demands made by the job, given such demands. assembly ("memory-safe") {
// size of (address(bytes20)+id(uint24/bytes3))
let size := 23
// keccack256(address+id(uint24)) to memory to generate jobKey
calldatacopy(0, 4, size)
jobKey := keccak256(0, size)
}
assembly ("memory-safe") {
// load jobAddress, cfg, and keeperId from calldata to the stack
actualKeeperId := shr(232, calldataload(28))
}
Keeper memory keeper = keepers[actualKeeperId];
uint256 binJob = getJobRaw(jobKey);
if (ConfigFlags.check(binJob, CFG_CHECK_KEEPER_MIN_CVP_DEPOSIT) && keepers[actualKeeperId].cvpStake < jobMinKeeperCvp[jobKey]) {
revert InsufficientJobScopedKeeperStake();
function execute_44g58pv() external
Asserts that if the job being executed is of one of two interval types, at least a full interval duration has elapsed since this job was last executed.
assembly ("memory-safe") {
// size of (address(bytes20)+id(uint24/bytes3))
let size := 23
// keccack256(address+id(uint24)) to memory to generate jobKey
calldatacopy(0, 4, size)
jobKey := keccak256(0, size)
}
uint256 binJob = getJobRaw(jobKey);
{
//the interval size occupies bytes 4-6, or bits 32-55. For this reason we first shift left by 32, making interval size the leading bytes, and then shift right by 232, retaining thereby only the first 24 bits (or exactly 3 bytes)
uint256 intervalSeconds = (binJob << 32) >> 232;
if (intervalSeconds > 0) {
//since lastExecutionAt is the leading byte and has field size bytes4 (uint32), we shift right by 224 and retain only the leading 32 bits, or 4 bytes
uint256 lastExecutionAt = binJob >> 224;
if (lastExecutionAt > 0) {
uint256 nextExecutionAt;
unchecked {
nextExecutionAt = lastExecutionAt + intervalSeconds;
}
if (nextExecutionAt > block.timestamp) {
revert IntervalNotReached(lastExecutionAt, intervalSeconds, block.timestamp);
}
}
}
}
function execute_44g58pv() external
Asserts that the message sender invoking
execute_44g58pv
is an externally-owned address (i.e. not an automatic contract). //if the sender is not an EoA, the origin will be the EoA which started the chain of transactions that led to the sender's activation and code execution and thereby the message transmission
if (msg.sender != tx.origin) {
revert NonEOASender();
}
function execute_44g58pv() external
The _calldata_source_within_0_2_assertion only makes sure the calldata numerical value is betwen 0 and 2. It will normally coincide with one of the three defined job types (SELECTOR, PRE_DEFINED, RESOLVER), but in a hypothetical case when this does not occur for any reason whatsoever, we need a catcher to raise an error.
assembly ("memory-safe") {
// size of (address(bytes20)+id(uint24/bytes3))
let size := 23
// keccack256(address+id(uint24)) to memory to generate jobKey
calldatacopy(0, 4, size)
jobKey := keccak256(0, size)
}
uint256 binJob = getJobRaw(jobKey);
CalldataSourceType calldataSource = CalldataSourceType((binJob << 56) >> 248);
if (calldataSource == CalldataSourceType.SELECTOR) {
...
} else if (calldataSource == CalldataSourceType.PRE_DEFINED) {
...
} else if (calldataSource == CalldataSourceType.RESOLVER) {
...
} else {
//catcher that should never be reached under normal operation
revert InvalidCalldataSource();
}
​
function execute_44g58pv() external
Asserts that the job has enough credits to cover the keeper's reward for executing it.
compensation = _calculateCompensation(ok, binJob, actualKeeperId, min, gasUsed);
uint256 creditsBefore = (binJob << 128) >> 168;
if (creditsBefore < compensation) {
if (ok) {
revert InsufficientJobCredits(creditsBefore, compensation);
}
}
function execute_44g58pv() external
This modifier asserts that the block base gas fee is either not in excess of the job-specified limit or accepted anyway by the appropriately configured flag
FLAG_ACCEPT_MAX_BASE_FEE_LIMIT
passed along with the execute call in the config
calldata byte. uint256 maxBaseFee;
unchecked {
maxBaseFee = ((binJob_ << 112) >> 240) * 1 gwei;
}
if (block.basefee > maxBaseFee && !ConfigFlags.check(cfg_, FLAG_ACCEPT_MAX_BASE_FEE_LIMIT)) {
revert BaseFeeGtGasPrice(block.basefee, maxBaseFee);
}
return maxBaseFee;
}
function _checkBaseFee(uint256 binJob_, uint256 cfg_) internal view virtual returns (uint256)
In some cases, job reverts will give traceback messages. Occasionally, however, they will give no traceback, and both occasion need to be handled properly. This code provides such a functionality. It is not properly a modifier, since it does not condition execution of anything and merely serves as an error handler, but we include it here on the basis of collecting on one page all revert-capable routines.
if (executionResponse_.length == 0) {
revert JobCallRevertedWithoutDetails();
} else {
assembly ("memory-safe") {
revert(add(32, executionResponse_), mload(executionResponse_))
}
}
function _afterExecutionReverted(
bytes32 jobKey_,
CalldataSourceType calldataSource_,
uint256 keeperId_,
bytes memory executionResponse_
) internal virtual
​
Asserts that the job owner has enough credits to cover the keeper's reward for executing it. Is only invoked when the
CFG_USE_JOB_OWNER_CREDITS
job config flag is set to True
. uint256 jobOwnerCreditsBefore = jobOwnerCredits[jobOwners[jobKey_]];
if (jobOwnerCreditsBefore < compensation_) {
if (ok_) {
revert InsufficientJobOwnerCredits(jobOwnerCreditsBefore, compensation_);
function _useJobOwnerCredits(bool ok_, bytes32 jobKey_, uint256 compensation_) internal
Asserts that the job owner has enough credits to cover the keeper's reward for executing it. Is only invoked when the
CFG_USE_JOB_OWNER_CREDITS
job config flag is set to True
. uint256 jobOwnerCreditsBefore = jobOwnerCredits[jobOwners[jobKey_]];
if (jobOwnerCreditsBefore < compensation_) {
if (ok_) {
revert InsufficientJobOwnerCredits(jobOwnerCreditsBefore, compensation_);
function _useJobOwnerCredits(bool ok_, bytes32 jobKey_, uint256 compensation_) internal
Asserts that the address of the supplied resolver is extant (i.e. nonzero).
if (resolver_.resolverAddress == address(0)) {
revert MissingResolverAddress();
}
function _setJobResolver(bytes32 jobKey_, Resolver calldata resolver_) internal
Asserts that the job for which credits are deposited has an owner (i.e. the owner is not a zero address).
if (jobOwners[jobKey_] == address(0)) {
revert JobWithoutOwner();
}
function _setJobResolver(bytes32 jobKey_, Resolver calldata resolver_) internal
Asserts that the job/job owner has enough credits (in native chain tokens) to withdraw the specifeid amount.
if (creditsBefore < amount_) {
revert CreditsWithdrawalUnderflow();
}
function withdrawJobCredits(
bytes32 jobKey_,
address payable to_,
uint256 amount_
) external
function withdrawJobOwnerCredits(address payable to_, uint256 amount_) external
Asserts that the registering keeper has a sufficient initial deposit value to stake at least the
minKeeperCvp
value demanded by the Agent parameter of the same name. if (initialDepositAmount_ < minKeeperCvp) {
revert InsufficientAmount();
}
function registerAsKeeper(address worker_, uint256 initialDepositAmount_) public virtual returns (uint256 keeperId)
Asserts that a keeper has accrued enough compensation in native chain tokens to withdraw the specified amount.
uint256 available = compensations[keeperId_];
if (amount_ > available) {
revert WithdrawAmountExceedsAvailable(amount_, available);
}