Job Registration & Update
This section describes the functional of the Agent contract on Job registration, update and removal.
Last updated
This section describes the functional of the Agent contract on Job registration, update and removal.
Last updated
To automate their smart contract execution, the Job provider should register the smart contract in the Agent and set the configuration of the Job.
The Job provider calls function registerJob(RegisterJobParams calldata params_, Resolver calldata resolver_, bytes calldata preDefinedCalldata_ )
to register a new job for execution.
RegisterJobParams calldata params_
: the parameters of the Job.
Resolver calldata resolver_
: the address of the resolver for the Job type RESOLVER and the calldata to be supplied at each invocation thereof.
bytes calldata preDefinedCalldata_
: the predefined calldata for the Job type PRE_DEFINED.
For each address of the Job there can be maximum of type(uint24).max = 2**24 - 1
unique job ids. This number should never be reached in any sane situation:
The maximum deposit amount is type(uint88).max
:
The Job address is specified and nonzero:
There are three available calldata sources (job types):
Reserved addresses are not allowed:
Jobs are enumerated by integer ids within each set of jobs with the same address. That is, if the job address already has 2 jobs, the new jobId = jobLastIds[params_.jobAddress] + 1
= 2. Afterwards, the jobLastIds
of this address is reassigned to equal the new job's id.
Each new job is checked for having nonzero base fee cap maxBaseFeeGwei
, and fixedReward_
and RewardPct_
are not zero simultaneously. In addition, checks are made to ensure that there is no mixing of job types: e.g., a resolver job may not have an interval specified, and, conversely, non-resolver jobs must have some interval length specified.
When all assertions are passed, a configuration byte for the job is made by bitwise addition of all flags multiplied by their specified Boolean values. Finally, a new instance of Job
is created and stored in the mapping(bytes32 => Job) internal jobs
.
The function emits event RegisterJob(bytes32 indexed jobKey, address indexed jobAddress, uint256 indexed jobId, address owner, RegisterJobParams params)
.
Should the Job provider desire to update his job, he ought to call function updateJob( bytes32 jobKey_, uint16 maxBaseFeeGwei_, uint16 rewardPct_, uint32 fixedReward_, uint256 jobMinCvp_, uint24 intervalSeconds_)
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order)
uint16 maxBaseFeeGwei_
: the upper bound on the base fee for gas to be used for computing the dynamic (gas-dependent) part of the keeper reward
uint16 rewardPct_
: the reward premium in percents; gives the dynamic (gas-dependent) reward
uint32 fixedReward_
: the fixed keeper reward. Mind that the fixed reward and the reward percentage may never be zero simultaneously (it is checked by _assertJobParams
).
uint256 jobMinCvp_
: minimal CVP stake admissible for a keeper allowed to execute this job
uint24 intervalSeconds_
: interval for the job execution
_assertOnlyJobOwner
asserts that the function caller is the owner of the job with the given key, i.e. prevents everyone but the owner from modifying the job
_assertJobParams
asserts that the maxBaseFeeGwei_
is specified and that either rewardPct_
or fixedReward_
is nonzero, i.e. the base fee is capped, and at least some reward is set.
_assertInterval
asserts that either the job is not of resolver type, and the interval not zero, or the job is of resolver type, and the interval is zero. This has to do with the different mechanisms of checking eligibility of interval and resolver jobs.
After the checks are performed, the flags of config are updated if need be (e.g. if the demand for minimal stake was made where before there was none), the parameters of the job (as an element of the mapping jobs
) are updated, and a corresponding event is emitted.
The function emits JobUpdate(jobKey_, maxBaseFeeGwei_, rewardPct_, fixedReward_, jobMinCvp_, intervalSeconds_);
Should the Job provider desire to set a resolver for his job, he ought to call the function setJobResolver(bytes32 jobKey_, Resolver calldata resolver_)
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order). Note: add this to the glossary for sure
Resolver calldata resolver_
: the Resolver
object (a struct of address resolverAddress
and bytes resolverCalldata
, encoding the address of the resolver and the data passable at invocation thereof respectively)
_assertOnlyJobOwner
asserts that the function caller is the owner of the job with the given key, i.e. prevents everyone but the owner from modifying the job
_assertJobCalldataSource
asserts that the calldata source of the job (i.e. its type) is indeed RESOLVER
, i.e. that one does not set a resolver for an interval problem, thereby engendering ambiguity
Just before setting the resolver address, a check is made that the address of the resolver supplied is not zero (i.e. not missing).
When all checks are passed, the mapping mapping(bytes32 => Resolver) internal resolvers
, which stores the resolvers indexed by job keys, sets the supplied resolver for the given job key.
Having set or updated the resolver, the function emits the event SetJobResolver(jobKey_, resolver_.resolverAddress, resolver_.resolverCalldata)
.
Should the Job provider desire to provide calldata for his job of the appropriate calldata source type, he ought to call the function setJobPreDefinedCalldata(bytes32 jobKey_, bytes calldata preDefinedCalldata_)
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order).
bytes calldata preDefinedCalldata_
: the calldata to set as predefined for the job
_assertOnlyJobOwner
asserts that the function caller is the owner of the job with the given key, i.e. prevents everyone but the owner from modifying the job
_assertJobCalldataSource
asserts that the calldata source of the job (i.e. its type) is indeed PRE_DEFINED
When all checks are passed, the mapping mapping(bytes32 => bytes) internal preDefinedCalldatas
, which stores the bytearrays of pre-defined calldata indexed by job keys, sets the supplied calldata for the given job key.
Having set or updated the calldata , the function emits the event SetJobPreDefinedCalldata(jobKey_, preDefinedCalldata_)
.
Should the Job provider desire to modify his job's config byte, he ought to call the function setJobConfig( bytes32 jobKey_, bool isActive_, bool useJobOwnerCredits_, bool assertResolverSelector_ )
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order).
bool isActive_
: indicates whether the job is active (i.e. scheduled for execution) under the new config
bool useJobOwnerCredits_
: indicates whether to use the credits belonging to job owner himself instead of credits belonging to a job to reward keepers under the new config
bool assertResolverSelector_
: indicates whether to enforce the selector of a resolver job function matching between the calldata passed by the Keeper and the job binary representation. This may be desirable because under the hood, resolver jobs work like PRE_DEFINED
jobs, except the calldata is provided by the Keeper. It would therefore be possible for a malicious Keeper to supply a different selector, which is not checkable unless this flag is set to True
.
_assertOnlyJobOwner
asserts that the function caller is the owner of the job with the given key, i.e. prevents everyone but the owner from modifying the job
When all checks are passed, a new config value is generated, initially being zero. It then has all the flags corresponding to parameters for which a True
value is passed added to it bitwise. The flags do not mix because they are given by distinct powers of 2. Then the binary job data is retrieved, bitwise multiplied with a mask that zeroes out the last (config) byte, and has the new config data added to it bitwise, in essence replacing the old trailing (config) byte with the new config byte. Finally, the job data is rewritten in memory.
Having updated the config byte, the function emits the event SetJobConfig(jobKey_, isActive_, useJobOwnerCredits_, assertResolverSelector_)
.
Should the Job provider desire to transfer his job to a new owner, he ought to call the function initiateJobTransfer(bytes32 jobKey_, address to_)
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order). It corresponds to the job being transferred.
address to_
: specifies the owner status recipient address
_assertOnlyJobOwner
asserts that the function caller is the owner of the job with the given key, i.e. prevents everyone but the owner from modifying the job
When all checks are passed, the pending owner address (the one specified) is entered to the mapping mapping(bytes32 => address) internal jobPendingTransfers
with a key given by the job key.
Having added the recipient address to the pending address mapping, the function emits the event InitiateJobTransfer(jobKey_, msg.sender, to_)
.
Upon transfer ownership initiation, it may be finalised when the recipient calls the function acceptJobTransfer(bytes32 jobKey_)
.
bytes32 jobKey_
: a job key, which is a keccak256 hash of a concatenation of the job's address and id (in that order). It corresponds to the job being transferred.
An assertion is made that the sender's address matches the pending owner address (i.e. the address retrievable from the mapping mapping(bytes32 => address) internal jobPendingTransfers
by the jobKey_
), that is to say, that the intended recipient calls this function and no one else
When all checks are passed, the pending owner address is removed from the jobPendingTransfers
(to be precise, the element indexed by the jobKey_
is removed therefrom, but the effect is the same), and the owner of the job indexed by jobKey_
is reset to be the pending (now actual) owner.
Having finalised the job transfer, the function emits the event AcceptJobTransfer(jobKey_, msg.sender)
, where msg.sender
, obviously, corresponds to the new job owner.