# Configuring Nodes Source: https://docs.chain.link/chainlink-nodes/configuring-nodes Starting with Chainlink v2.0.0, TOML configuration is the only supported configuration method. You can switch to TOML format using the [Chainlink v1.13.0 image](https://hub.docker.com/r/smartcontract/chainlink/tags). After your Chainlink node is running stable on v1.13.0, you can continue to use the TOML config on future images where support for `.env` configs are no longer supported. You can see the available config options on the [Node Config](/chainlink-nodes/v1/node-config) and [Node Secrets](/chainlink-nodes/v1/secrets-config) pages. ## Migrating from environment variables to TOML Before you begin, update your Chainlink Node to v1.13.0 and ensure that it operates as expected. This is the last version that supports environment variable configuration, and the best version to use for TOML migration before you update the node to future versions. ### Export your current config You can generate the `config.toml` file using the `chainlink config dump` command. This guide shows you how to do this for a Chainlink node running in a Docker container: 1. Open an interactive shell inside the Docker container that runs your node: ```shell docker exec -it chainlink bash ``` 2. Log in to the Chainlink CLI with an account that has admin access: ```shell chainlink admin login ``` 3. Export the current config to a `config.toml` file: ```shell chainlink config dump > config.toml ``` 4. Log out of the Chainlink CLI and close the shell on the Docker container. ```shell chainlink admin logout && exit ``` 5. Change to the directory where your .env file is located. This is the directory that you mount to Docker when you run the node. You will create your `config.toml` and `secrets.toml` files here so Docker can provide them to your node container. ```shell cd ~/.chainlink ``` 6. Write the config file from the container to your host: ```shell docker exec -it chainlink cat /home/chainlink/config.toml > ./config.toml ``` 7. Create a `secrets.toml` file with the minimum required secrets, which are `[Database]` and `[Password]`. If you are working on a test node, you can use `?sslmode=disable` in the database `URL`. You might also need [`AllowSimplePasswords = true`](/chainlink-nodes/v1/secrets-config#allowsimplepasswords) in the `[Database]` section so you can start the node, but you should make the database password sufficiently complex as a best practice: ```shell echo "[Database] URL = 'postgresql://user:pass@localhost:5432/dbname' [Password] Keystore = 'keystore_pass'" > ./secrets.toml ``` 8. Edit the `secrets.toml` file to include the secrets you need for your specific Chainlink node. See the [Node Secrets](/chainlink-nodes/v1/secrets-config) page for a full list of available options. ### Validate the configuration After you have the `config.toml` file and the `secrets.toml` files on the host system, you can validate these files using a temporary Docker container. 1. Validate the configuration by running the `config validate` command in the Docker container. This command changes to `node validate` when you upgrade your node to version `2.0.0` or later. ```shell docker run --platform linux/x86_64/v8 --name chainlink-config-validator -v ~/.chainlink:/chainlink -it --rm smartcontract/chainlink:1.13.0 -config /chainlink/config.toml -secrets /chainlink/secrets.toml config validate ``` You will likely see some invalid config errors. For example: ```shell Invalid configuration: EVM.3.Nodes: missing: must have at least one node ``` 2. Edit the `config.toml` and `secrets.toml` files to manually correct any errors. See the [Node Config](/chainlink-nodes/v1/node-config) and [Node Secrets](/chainlink-nodes/v1/secrets-config) pages to learn which settings are valid. For the error in this example, an EVM chain was configured with no nodes. Removing this from the config made the config valid: ``` [[EVM]] ChainID = '421613' Enabled = false Nodes = [] ``` 3. Run the `config validate` command again and make additional changes until you have a valid config message: ```shell Valid configuration. ``` ### Restart your Chainlink node using the TOML config With your valid config and secrets files, you can migrate your Chainlink node to use the new config. 1. Stop your existing Chainlink node: ```shell docker stop chainlink ``` 2. Make a Postgres [database snapshot](https://www.postgresql.org/docs/current/backup-dump.html) so you can restore your previous Chainlink node if necessary. 3. Start a new Chainlink node Docker container named using the new `config.toml` and `secrets.toml` files. This example uses `chainlink-toml` as the container name: ```shell docker run --platform linux/x86_64/v8 --name chainlink-toml -v ~/.chainlink:/chainlink -it -p 6688:6688 --add-host=host.docker.internal:host-gateway smartcontract/chainlink:1.13.0 -config /chainlink/config.toml -secrets /chainlink/secrets.toml node start ``` Test your node to verify that it works as intended. If you are using a VPS, open an [SSH tunnel](https://www.howtogeek.com/168145/how-to-use-ssh-tunneling/) using `ssh -i $KEY $USER@$REMOTE-IP -L 6688:localhost:6688 -N`. Connect to the Operator UI in your browser at `localhost:6688`. ## Using multiple config files You can use multiple config and secrets files. The config settings from each file are applied in the order that you specify when you run your node. Duplicated fields override values specified in earlier config files. This allows you to create a common config that applies to many nodes with specific configs for nodes that need unique configuration settings. Specifying multiple secrets files is invalid. To specify multiple config files, add additional `-config` flags to the `docker run` command: ```shell docker run --platform linux/x86_64/v8 --name chainlink -v ~/.chainlink:/chainlink -it -p 6688:6688 --add-host=host.docker.internal:host-gateway smartcontract/chainlink:1.13.0 -config /chainlink/config.toml -config /chainlink/config2.toml -config /chainlink/config3.toml -secrets /chainlink/secrets.toml node start ``` --- # Contract addresses Source: https://docs.chain.link/chainlink-nodes/contracts/addresses This page lists the [operator factory](/chainlink-nodes/contracts/operatorfactory) addresses for different networks. ## Ethereum ### Mainnet ### Sepolia --- # Forwarder Source: https://docs.chain.link/chainlink-nodes/contracts/forwarder In the EVM world, [externally-owned account](https://ethereum.org/en/developers/docs/accounts/) transactions are confirmed sequentially. Chainlink nodes that use a single externally-owned account (EOA) per chain face several important challenges: - Transactions are broadcast through a single FIFO queue, so the throughput is limited to a single lane. Low throughput is terrible for the overall user experience because concurrent clients must wait to have their requests fulfilled. When more clients make requests, the longer it takes for requests to be fulfilled. - Transactions are not executed by priority. For example, consider a situation where there are two transactions in a row. The first transaction is a fulfillment of an API request to get the winner of the FIFA world cup 2022, and the second transaction is an *ETH/USD* price feed update used by multiple DeFi protocols. Relying on a single EOA forces the second transaction to be confirmed only after the first transaction is fulfilled. The first transaction is not as time-sensitive, but it is still fulfilled first. - A stuck transaction will cause all the subsequent transactions to remain pending. For example, if a transaction becomes stuck because the gas price is not set high enough, that transaction must be bumped or canceled before subsequent transactions can be fulfilled. - As a workaround, some node operators deploy multiple Chainlink nodes per chain. While this allows them to handle different types of requests separately (one node for price feeds and another to fulfill API requests), this comes with an overhead in infrastructure and maintenance costs. To solve these challenges, we introduced two major features that will allow node operators to set up different transaction-sending strategies more securely while lowering their infrastructure costs: - Chainlink nodes support multiple EOAs. - [Forwarder](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/AuthorizedForwarder.sol) contracts allow a node operator to manage multiple EOAs and make them look like a single address. If you use a web2 analogy, forwarder contracts act like a reverse proxy server where the user is served by the same address and does not see which server the traffic is coming from. To do so, nodes call the [forward](#forward) function on the forwarder contract. Combining multiple EOAs and forwarder contracts allows greater flexibility and security in terms of design: - Node operators can expand horizontally using multiple EOAs. They can deploy one or multiple forwarder contracts for these EOAs. The combination of EOAs and forwarders offers a lot of flexibility for setting up different pipelines for handling transactions. - Node operators can support different job types (OCR, VRF, API request..Etc) on the same node, which reduces maintenance and infrastructure costs. - Security-wise, forwarder contracts distinguish between owner accounts and authorized sender accounts. Authorized senders are hot wallets such as the EOAs of a Chainlink node. If a node is compromised, the owner is responsible for changing the authorized senders list. - Node operators do not need to manually compile and deploy [operator](/chainlink-nodes/contracts/operator) and [forwarder](/chainlink-nodes/contracts/forwarder) contracts. They can deploy them directly from the [operator factory](/chainlink-nodes/contracts/operatorfactory) by calling the [deploynewoperatorandforwarder](/chainlink-nodes/contracts/operatorfactory#deploynewoperatorandforwarder) function. From a design perspective, the owner of a forwarder contract is an [operator](/chainlink-nodes/contracts/operator) contract. The owner of the operator contract is usually a more secure address with keys stored in a hardware wallet or protected by a multisig. Node operators can manage a set of forwarder contracts through an operator contract. ## API Reference The forwarder contract inherits [AuthorizedReceiver.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/AuthorizedReceiver.sol) and [ConfirmedOwnerWithProposal.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol). Read the [Receiver](/chainlink-nodes/contracts/receiver) and [Ownership](/chainlink-nodes/contracts/ownership) API references to learn more. ### Methods #### typeAndVersion ```solidity function typeAndVersion() external pure virtual returns (string) ``` The type and version of this contract. ##### Return Values | Name | Type | Description | | ---- | ------ | ----------------------- | | | string | Type and version string | #### forward ```solidity function forward(address to, bytes data) external ``` Forward a call to another contract. *Only callable by an authorized sender* ##### Parameters | Name | Type | Description | | ---- | ------- | ----------- | | to | address | address | | data | bytes | to forward | #### ownerForward ```solidity function ownerForward(address to, bytes data) external ``` Forward a call to another contract. *Only callable by the owner* ##### Parameters | Name | Type | Description | | ---- | ------- | ----------- | | to | address | address | | data | bytes | to forward | #### transferOwnershipWithMessage ```solidity function transferOwnershipWithMessage(address to, bytes message) external ``` Transfer ownership with instructions for recipient.Emit [OwnershipTransferRequestedWithMessage](#ownershiptransferrequestedwithmessage) event. ##### Parameters | Name | Type | Description | | ------- | ------- | --------------------------------------------------- | | to | address | address proposed recipient of ownership | | message | bytes | instructions for recipient upon accepting ownership | ### Events #### OwnershipTransferRequestedWithMessage ```solidity event OwnershipTransferRequestedWithMessage(address from, address to, bytes message) ``` --- # Operator Source: https://docs.chain.link/chainlink-nodes/contracts/operator Oracles must deploy an onchain contract to handle requests made through the LINK token (Read [Basic Request Model](/architecture-overview/architecture-request-model) to learn more). When the *Basic Request* model was introduced, node operators had to deploy the legacy [Oracle contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.4/Oracle.sol). However, these come with some limitations, and soon, we introduced [operator contracts](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol). In addition to replacing oracle contracts, operator contracts come with additional features that add more security and flexibility for node operators. ## Features ### Multi-word response In the EVM architecture, a word is made up of 32 bytes. One limitation of the [Oracle.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.4/Oracle.sol) contract is that it limits responses to requests to 32 bytes. [Operator.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) doesn't have the same limitation as it supports a response made of multiple EVM words. ### Factory deployment To deploy an *Oracle* contract, each node operator has to manually compile and deploy [Oracle.sol](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.4/Oracle.sol). The vast number of Solidity versions and steps involved in verifying the contract made it difficult for a client to verify that the deployed contract had not been tampered with. To fix this, node operators can use a [factory](/chainlink-nodes/contracts/operatorfactory) to deploy an instance of the *operator* contract. Moreover, the factory exposes a getter for clients to check if it deployed a specific *operator* contract address. ### Distributing funds to multiple addresses A common pain point of node operators is keeping their addresses funded. *Operator*'s `distributeFunds` method allows node operators to fund multiple addresses in a single transaction. ### Flexibility and security By using multiple externally-owned accounts (EOAs) on Chainlink nodes and [forwarder](/chainlink-nodes/contracts/forwarder) contracts, node operators can set up different transaction-sending strategies. As discussed in the [forwarder](/chainlink-nodes/contracts/forwarder) contracts page: - Chainlink nodes' EOAs are hot wallets that fulfill requests. - These EOAs can be associated with one or multiple [forwarder](/chainlink-nodes/contracts/forwarder) contracts. The forwarder's owner must whitelist them to call the [forward](/chainlink-nodes/contracts/forwarder#forward) function. One operator contract owns one or multiple forwarder contracts. - Node operators manage their forwarder contracts through operator contracts. They use a secure wallet such as hardware or a multisig wallet as the operator's owner account. ## API reference The operator contract inherits [AuthorizedReceiver](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/AuthorizedReceiver.sol) and [ConfirmedOwnerWithProposal](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol). Read [AuthorizedReceiver](/chainlink-nodes/contracts/receiver) and [ConfirmedOwnerWithProposal](/chainlink-nodes/contracts/ownership) API references. ### Methods #### oracleRequest ```solidity function oracleRequest(address sender, uint256 payment, bytes32 specId, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 dataVersion, bytes data) external ``` Creates the Chainlink request. This is backwards compatible API with [Oracle.sol contracts](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.0.0/contracts/src/v0.4/Oracle.sol), but the behavior changes because `callbackAddress` is assumed to be the same as the request sender. ##### Parameters | Name | Type | Description | | ------------------ | ------- | ---------------------------------------------- | | sender | address | The sender of the request | | payment | uint256 | The amount of payment given (specified in wei) | | specId | bytes32 | The Job Specification ID | | callbackAddress | address | The consumer of the request | | callbackFunctionId | bytes4 | The callback function ID for the response | | nonce | uint256 | The nonce sent by the requester | | dataVersion | uint256 | The specified data version | | data | bytes | The extra request parameters | #### operatorRequest ```solidity function operatorRequest(address sender, uint256 payment, bytes32 specId, bytes4 callbackFunctionId, uint256 nonce, uint256 dataVersion, bytes data) external ``` Creates the Chainlink request. Stores the hash of the params as the onchain commitment for the request. Emits [OracleRequest](#oraclerequest-1) event for the Chainlink node to detect. ##### Parameters | Name | Type | Description | | ------------------ | ------- | ---------------------------------------------- | | sender | address | The sender of the request | | payment | uint256 | The amount of payment given (specified in wei) | | specId | bytes32 | The Job Specification ID | | callbackFunctionId | bytes4 | The callback function ID for the response | | nonce | uint256 | The nonce sent by the requester | | dataVersion | uint256 | The specified data version | | data | bytes | The extra request parameters | #### fulfillOracleRequest ```solidity function fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data) external returns (bool) ``` Called by the Chainlink node to fulfill requests. Given params must hash back to the commitment stored from `oracleRequest`. Will call the callback address' callback function without bubbling up error checking in a `require` so that the node can get paid. Emits [OracleResponse](#oracleresponse) event. ##### Parameters | Name | Type | Description | | ------------------ | ------- | ------------------------------------------------------------------------------ | | requestId | bytes32 | The fulfillment request ID that must match the requester's | | payment | uint256 | The payment amount that will be released for the oracle (specified in wei) | | callbackAddress | address | The callback address to call for fulfillment | | callbackFunctionId | bytes4 | The callback function ID to use for fulfillment | | expiration | uint256 | The expiration that the node should respond by before the requester can cancel | | data | bytes32 | The data to return to the consuming contract | ##### Return values | Name | Type | Description | | ---- | ---- | ------------------------------------------ | | | bool | Status if the external call was successful | #### fulfillOracleRequest2 ```solidity function fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes data) external returns (bool) ``` Called by the Chainlink node to fulfill requests with multi-word support. Given params must hash back to the commitment stored from `oracleRequest`. Will call the callback address' callback function without bubbling up error checking in a `require` so that the node can get paid. Emits [OracleResponse](#oracleresponse) event. ##### Parameters | Name | Type | Description | | ------------------ | ------- | ------------------------------------------------------------------------------ | | requestId | bytes32 | The fulfillment request ID that must match the requester's | | payment | uint256 | The payment amount that will be released for the oracle (specified in wei) | | callbackAddress | address | The callback address to call for fulfillment | | callbackFunctionId | bytes4 | The callback function ID to use for fulfillment | | expiration | uint256 | The expiration that the node should respond by before the requester can cancel | | data | bytes | The data to return to the consuming contract | ##### Return values | Name | Type | Description | | ---- | ---- | ------------------------------------------ | | | bool | Status if the external call was successful | #### transferOwnableContracts ```solidity function transferOwnableContracts(address[] ownable, address newOwner) external ``` Transfer the ownership of ownable contracts. This is primarily intended for authorized forwarders but could possibly be extended to work with future contracts. ##### Parameters | Name | Type | Description | | -------- | ---------- | -------------------------------- | | ownable | address[] | list of addresses to transfer | | newOwner | address | address to transfer ownership to | #### acceptOwnableContracts ```solidity function acceptOwnableContracts(address[] ownable) public ``` Accept the ownership of an ownable contract. This is primarily intended for authorized forwarders but could possibly be extended to work with future contracts. Emits [OwnableContractAccepted](#ownablecontractaccepted) event. *Must be the pending owner on the contract* ##### Parameters | Name | Type | Description | | ------- | ---------- | ------------------------------------------------ | | ownable | address[] | list of addresses of Ownable contracts to accept | #### setAuthorizedSendersOn ```solidity function setAuthorizedSendersOn(address[] targets, address[] senders) public ``` Sets the fulfillment permission for `senders` on `targets`. Emits [TargetsUpdatedAuthorizedSenders](#targetsupdatedauthorizedsenders) event. ##### Parameters | Name | Type | Description | | ------- | ---------- | ---------------------------------------------- | | targets | address[] | The addresses to set permissions on | | senders | address[] | The addresses that are allowed to send updates | #### acceptAuthorizedReceivers ```solidity function acceptAuthorizedReceivers(address[] targets, address[] senders) external ``` Accepts ownership of ownable contracts and then immediately sets the authorized sender list on each of the newly owned contracts. This is primarily intended for authorized forwarders but could possibly be extended to work with future contracts. ##### Parameters | Name | Type | Description | | ------- | ---------- | ---------------------------------------------- | | targets | address[] | The addresses to set permissions on | | senders | address[] | The addresses that are allowed to send updates | #### withdraw ```solidity function withdraw(address recipient, uint256 amount) external ``` Allows the node operator to withdraw earned LINK to a given address `recipient`. *The owner of the contract can be another wallet and does not have to be a Chainlink node* ##### Parameters | Name | Type | Description | | --------- | ------- | ------------------------------------- | | recipient | address | The address to send the LINK token to | | amount | uint256 | The amount to send (specified in wei) | #### withdrawable ```solidity function withdrawable() external view returns (uint256) ``` Displays the amount of LINK that is available for the node operator to withdraw. *We use `1` in place of 0 in storage* ##### Return values | Name | Type | Description | | ---- | ------- | ----------------------------------------------- | | | uint256 | The amount of withdrawable LINK on the contract | #### ownerForward ```solidity function ownerForward(address to, bytes data) external ``` Forward a call to another contract. *Only callable by the owner* ##### Parameters | Name | Type | Description | | ---- | ------- | ----------- | | to | address | address | | data | bytes | to forward | #### ownerTransferAndCall ```solidity function ownerTransferAndCall(address to, uint256 value, bytes data) external returns (bool success) ``` Interact with other LinkTokenReceiver contracts by calling transferAndCall. ##### Parameters | Name | Type | Description | | ----- | ------- | ------------------------------------------------------ | | to | address | The address to transfer to. | | value | uint256 | The amount to be transferred. | | data | bytes | The extra data to be passed to the receiving contract. | ##### Return values | Name | Type | Description | | ------- | ---- | ----------- | | success | bool | bool | #### distributeFunds ```solidity function distributeFunds(address payable[] receivers, uint256[] amounts) external payable ``` Distribute funds to multiple addresses using ETH sent to this payable function. Array length must be equal, ETH sent must equal the sum of amounts. A malicious receiver could cause the distribution to revert, in which case it is expected that the address is removed from the list. ##### Parameters | Name | Type | Description | | --------- | ------------------ | ----------------- | | receivers | address payable[] | list of addresses | | amounts | uint256[] | list of amounts | #### cancelOracleRequest ```solidity function cancelOracleRequest(bytes32 requestId, uint256 payment, bytes4 callbackFunc, uint256 expiration) external ``` Allows recipient to cancel requests sent to this oracle contract. Will transfer the LINK sent for the request back to the recipient address. Given params must hash to a commitment stored on the contract in order for the request to be valid. Emits [CancelOracleRequest](#canceloraclerequest-1) event. ##### Parameters | Name | Type | Description | | ------------ | ------- | ---------------------------------------------------- | | requestId | bytes32 | The request ID | | payment | uint256 | The amount of payment given (specified in wei) | | callbackFunc | bytes4 | The requester's specified callback function selector | | expiration | uint256 | The time of the expiration for the request | #### cancelOracleRequestByRequester ```solidity function cancelOracleRequestByRequester(uint256 nonce, uint256 payment, bytes4 callbackFunc, uint256 expiration) external ``` Allows requester to cancel requests sent to this oracle contract. Will transfer the LINK sent for the request back to the recipient address. Given params must hash to a commitment stored on the contract in order for the request to be valid. Emits [CancelOracleRequest](#canceloraclerequest-1) event. ##### Parameters | Name | Type | Description | | ------------ | ------- | ---------------------------------------------------- | | nonce | uint256 | The nonce used to generate the request ID | | payment | uint256 | The amount of payment given (specified in wei) | | callbackFunc | bytes4 | The requester's specified callback function selector | | expiration | uint256 | The time of the expiration for the request | #### getChainlinkToken ```solidity function getChainlinkToken() public view returns (address) ``` Returns the address of the LINK token This is the public implementation for chainlinkTokenAddress, which is an internal method of the ChainlinkClient contract. ### Events #### OracleRequest ```solidity event OracleRequest(bytes32 specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data) ``` #### CancelOracleRequest ```solidity event CancelOracleRequest(bytes32 requestId) ``` #### OracleResponse ```solidity event OracleResponse(bytes32 requestId) ``` #### OwnableContractAccepted ```solidity event OwnableContractAccepted(address acceptedContract) ``` #### TargetsUpdatedAuthorizedSenders ```solidity event TargetsUpdatedAuthorizedSenders(address[] targets, address[] senders, address changedBy) ``` --- # Operator Factory Source: https://docs.chain.link/chainlink-nodes/contracts/operatorfactory The [factory](https://www.youtube.com/watch?v=Q1zZo4O_Ong) design pattern is a well know programming pattern: Rather than compiling and creating instances of a contract yourself, the *factory* does it for you. As explained in the [operator guide](/chainlink-nodes/contracts/operator), the `OperatorFactory` contract comes with these benefits: - Node operators do not need to manually compile and deploy [operator](/chainlink-nodes/contracts/operator) or/and [forwarder](/chainlink-nodes/contracts/forwarder) contracts. They can deploy them directly from the factory. See the[deploynewoperator](#deploynewoperator), [deploynewforwarder](#deploynewforwarder), and [deploynewoperatorandforwarder](#deploynewoperatorandforwarder) functions. - Clients can verify if the factory deployed a given contract. See the [created](#created) function. ## API Reference ### Methods #### typeAndVersion ```solidity function typeAndVersion() external pure virtual returns (string) ``` The type and version of this contract. ##### Return values | Name | Type | Description | | ---- | ------ | ----------------------- | | | string | Type and version string | #### deployNewOperator ```solidity function deployNewOperator() external returns (address) ``` Creates a new operator contract with the msg.sender as owner. Emits `OperatorCreated` event. #### deployNewOperatorAndForwarder ```solidity function deployNewOperatorAndForwarder() external returns (address, address) ``` Creates a new operator contract with the msg.sender as the owner and a new forwarder with the operator as the owner. Emits: - [OperatorCreated](#operatorcreated) event. - [AuthorizedForwarderCreated](#authorizedforwardercreated) event. #### deployNewForwarder ```solidity function deployNewForwarder() external returns (address) ``` Creates a new forwarder contract with the msg.sender as owner. Emits [AuthorizedForwarderCreated](#authorizedforwardercreated) event. #### deployNewForwarderAndTransferOwnership ```solidity function deployNewForwarderAndTransferOwnership(address to, bytes message) external returns (address) ``` Creates a new forwarder contract with the msg.sender as owner. Emits [AuthorizedForwarderCreated](#authorizedforwardercreated) event. #### created ```solidity function created(address query) external view returns (bool) ``` Indicates whether this factory deployed an address. ### Events #### OperatorCreated ```solidity event OperatorCreated(address operator, address owner, address sender) ``` #### AuthorizedForwarderCreated ```solidity event AuthorizedForwarderCreated(address forwarder, address owner, address sender) ``` --- # Ownership Source: https://docs.chain.link/chainlink-nodes/contracts/ownership [ConfirmedOwnerWithProposal](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol) is inherited by [operator](/chainlink-nodes/contracts/operator) and [forwarder](/chainlink-nodes/contracts/forwarder) contracts. It contains helpers for basic contract ownership. ## API reference ### Methods #### transferOwnership ```solidity function transferOwnership(address to) public ``` Allows an owner to begin transferring ownership to a new address. Emits an [OwnershipTransferRequested](#ownershiptransferrequested) event. #### acceptOwnership ```solidity function acceptOwnership() external ``` Allows an ownership transfer to be completed by the recipient. Emits an [OwnershipTransferred](#ownershiptransferred) event. #### owner ```solidity function owner() public view returns (address) ``` Get the current owner. ### Events #### OwnershipTransferRequested ```solidity event OwnershipTransferRequested(address from, address to) ``` #### OwnershipTransferred ```solidity event OwnershipTransferred(address from, address to) ``` --- # Receiver Source: https://docs.chain.link/chainlink-nodes/contracts/receiver [AuthorizedReceiver](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/AuthorizedReceiver.sol) is an abstract contract inherited by [operator](/chainlink-nodes/contracts/operator) and [forwarder](/chainlink-nodes/contracts/forwarder) contracts. ## API reference ### Methods #### setAuthorizedSenders ```solidity function setAuthorizedSenders(address[] senders) external ``` Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. Emits an [AuthorizedSendersChanged](#authorizedsenderschanged) event. ##### Parameters | Name | Type | Description | | ------- | ---------- | ---------------------------------------------- | | senders | address[] | The addresses of the authorized Chainlink node | #### getAuthorizedSenders ```solidity function getAuthorizedSenders() external view returns (address[]) ``` Retrieve a list of authorized senders. ##### Return values | Name | Type | Description | | ---- | ---------- | ------------------ | | | address[] | array of addresses | #### isAuthorizedSender ```solidity function isAuthorizedSender(address sender) public view returns (bool) ``` Use this to check if a node is authorized to fulfill requests. ##### Parameters | Name | Type | Description | | ------ | ------- | --------------------------------- | | sender | address | The address of the Chainlink node | ##### Return values | Name | Type | Description | | ---- | ---- | ------------------------------------ | | | bool | The authorization status of the node | ### Events #### AuthorizedSendersChanged ```solidity event AuthorizedSendersChanged(address[] senders, address changedBy) ``` --- # Building External Initiators Source: https://docs.chain.link/chainlink-nodes/external-initiators/building-external-initiators An external initiator can trigger a run for any webhook job that it has been linked to. The URL for triggering a run is such: ```shell curl -X POST -H "Content-Type: application/json" --data '{"myKey": "myVal"}' http://localhost:6688/v2/jobs//runs ``` You will need to specify two headers: 1. "X-Chainlink-EA-AccessKey" 2. "X-Chainlink-EA-Secret" ## JSON jobs (REMOVED) We will be using the Chainlink external initiator repo for reference. You can see some examples of existing initiators in the blockchain folder. External initiators are simple web initiators that can be activated by any job instead of just one. To set one up, you need to have a service similar to an external adapter that sends an `HTTPPost` message runs API call to your chainlink node service. Here is a sample URL for a web job could look like: ```shell curl -b cookiefile -X POST -H "Content-Type: application/json" --data '{"myKey":"myVal"}' http://localhost:6688/v2/jobs/%s/runs ``` Where `%s` is the jobId. External initiators make the same API call, with 2 added headers: 1. "X-Chainlink-EA-AccessKey" 2. "X-Chainlink-EA-Secret" These are keys generated when you register your external initiator with your node. Triggering a run through an external initiator is as simple as making this API call to your node. All jobs with this EI configured will then be kicked off in this way. A simple external initiator in pseudo code could look like this: ```text while(True): send_api_call_with_external_initiator_access_key_headers() sleep(4) ``` And have this job run on the same machine as your node. --- # Adding External Initiators to Nodes Source: https://docs.chain.link/chainlink-nodes/external-initiators/external-initiators-in-nodes ## Creating an external initiator To create an external initiator you must use the remote API. You can do this yourself, like so: ```text POST http:///v2/external_initiators -d ``` where payload is a JSON blob that contains: ```json { "name": , "url": } ``` If a URL is provided, Chainlink will notify this URL of added and deleted jobs that can be triggered by this external initiator. This allows the external initiator to program in certain actions e.g. subscribing/unsubscribing to logs based on the job, etc. On creation: ```text POST -d {"jobId": , "type": , "params": } ``` On deletion: ```text DELETE / ``` You can use the chainlink client for convenience to access this API. Enter the [Chainlink nodes CLI](/chainlink-nodes/resources/miscellaneous/#execute-commands-running-docker) and run the following command ```shell chainlink initiators create ``` `NAME`: The name you want to use for your external initiator. `URL`: The URL of your jobs endpoint. ie: `http://172.17.0.1:8080/jobs` This will give you the environment variables you need to run your external initiator. Copy the output. It will look something like this: ``` ║ ei_name ║ http://localhost:8080/jobs ║ a4846e85727e46b48889c6e28b555696 ║ dnNfNhiiCTm1o6l+hGJVfCtRSSuDfZbj1VO4BkZG3E+b96lminE7yQHj2KALMAIk ║ iWt64+Q9benOf5JuGwJtQnbByN9rtHwSlElOVpHVTvGTP5Zb2Guwzy6w3wflwyYt ║ 56m38YkeCymYU0kr4Yg6x3e98CyAu+37y2+kMO2AL9lRMjA3hRA1ejFdG9UfFCAE ``` Be sure to save these values, since the secrets cannot be shown again. You now can use `ei_name` as an initiator in your jobspec. Set a new `.env` file, and add the respective values ```text EI_DATABASEURL=postgresql://$USERNAME:$PASSWORD@$SERVER:$PORT/$DATABASE EI_CHAINLINKURL=http://localhost:6688 EI_IC_ACCESSKEY= EI_IC_SECRET= EI_CI_ACCESSKEY= EI_CI_SECRET= ``` At the time of writing, the output should be in order. For example, in from the output above, `EI_IC_ACCESSKEY=a4846e85727e46b48889c6e28b555696` and so on. Start your EI. Whatever code you used to run your external initiator, pass it the new headers created for the access headers, and then start your service. An easy way to do this is by having it read from the `.env` file you just created. Check out the Conflux External initiator for an example. You'll want to test that your job is running properly. Meeting the criteria of your EI and then checking to see if a sample job kicks off is the best way to test this. To try a real-life example, feel free to follow along with the Conflux EI demo. ## Deleting an external initiator To delete an external initiator you must use the remote API. You can do this yourself, like so: ```text DELETE http:///v2/external_initiators/ ``` You can alternatively use the chainlink client for convenience: ```shell chainlink initiators destroy ``` ## Listing external initiators To see your installed external initiators: ```text GET http:///v2/external_initiators?size=100&page=1 ``` Or, using the chainlink client: ```shell chainlink initiators list ``` --- # Introduction Source: https://docs.chain.link/chainlink-nodes/external-initiators/external-initiators-introduction External initiators allow jobs in a node to be initiated depending on some external condition. The ability to create and add external initiators to Chainlink nodes enables blockchain agnostic cross-chain compatibility. Initiator Bridges handle the authentication to and from the External Initiator and where to send the messages. When creating a Bridge two parameters are required: Only the [webhook](/chainlink-nodes/oracle-jobs/all-jobs/#webhook-jobs) job type can be initiated using an External Initiator. The external initiator must be created before the webhook job, and must be referenced by name (whitelisted) in order for that external initiator to be allowed to trigger the given webhook job. When the External Initiator is created it generates two pairs of credentials: Outgoing and Incoming. The Outgoing Access Key and Secret are used to authenticate messages sent from the Core to the External Initiator. The Incoming Access Key and Secret are used to authenticate messages sent from the External Initiator to the Core. Then, once you've created the name, bridge, and have the correct access keys for the URL, you can proceed to use the external initiator as if it's a regular initiator in future job specs. For how to create an external initiator see [adding external initiators to nodes](/chainlink-nodes/external-initiators/external-initiators-in-nodes). --- # Existing Job Example specs Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-existing-job This is an example v2 (TOML) job spec for returning gas price using [etherscan](https://docs.etherscan.io/api-endpoints/gas-tracker#get-gas-oracle) in one Chainlink API Call. Note that the job : - Uses an [external adapter](/chainlink-nodes/external-adapters/external-adapters) to consume the etherscan API: [EtherScan External Adapter](https://github.com/smartcontractkit/external-adapters-js/tree/develop/packages/sources/etherscan). Note that this is done using the [bridge](/chainlink-nodes/oracle-jobs/all-tasks/#bridge-task) task: `type="bridge" name="etherscan"`. - Calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. To test this job spec from a smart contract, see this [Example](/any-api/get-request/examples/existing-job-request). ```toml type = "directrequest" schemaVersion = 1 name = "Etherscan gas price" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] etherscanFast [type="bridge" name="etherscan" requestData="{\\"data\\": {\\"endpoint\\": \\"gasprice\\", \\"speed\\":\\"fast\\" }}"] etherscanAverage [type="bridge" name="etherscan" requestData="{\\"data\\": {\\"endpoint\\": \\"gasprice\\", \\"speed\\":\\"medium\\" }}"] etherscanSafe [type="bridge" name="etherscan" requestData="{\\"data\\": {\\"endpoint\\": \\"gasprice\\", \\"speed\\":\\"safe\\" }}"] decode_log -> etherscanFast decode_log -> etherscanAverage decode_log -> etherscanSafe gasPriceFast [type=jsonparse path="data,result"] gasPriceAverage [type=jsonparse path="data,result"] gasPriceSafe [type=jsonparse path="data,result"] etherscanFast -> gasPriceFast etherscanAverage -> gasPriceAverage etherscanSafe -> gasPriceSafe gasPriceFast -> encode_data gasPriceAverage -> encode_data gasPriceSafe -> encode_data encode_data [type=ethabiencode abi="(bytes32 _requestId, uint256 _fastPrice, uint256 _averagePrice, uint256 _safePrice)" data="{\\"_requestId\\": $(decode_log.requestId),\\"_fastPrice\\": $(gasPriceFast),\\"_averagePrice\\": $(gasPriceAverage),\\"_safePrice\\": $(gasPriceSafe)}"] encode_tx [type=ethabiencode abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"] submit_tx [type=ethtx to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] encode_data -> encode_tx -> submit_tx """ ``` --- # GET > Bool Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-get-bool This is an example v2 (TOML) job spec for calling any public API, parsing the result then returning a *bool* in one Chainlink API Call. Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. ```toml type = "directrequest" schemaVersion = 1 name = "Get > Bool - (TOML)" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"] parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"] encode_data [type="ethabiencode" abi="(bytes32 requestId, bool value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(parse) }"] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}" ] submit_tx [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch -> parse -> encode_data -> encode_tx -> submit_tx """ ``` --- # GET > Bytes Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-get-bytes This is an example v2 (TOML) job spec for returning *bytes* in one Chainlink API Call. Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. To test it from a smart contract, see this [Example](/any-api/get-request/examples/large-responses). ```toml type = "directrequest" schemaVersion = 1 name = "Get > Bytes" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"] parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"] encode_large [type="ethabiencode" abi="(bytes32 requestId, bytes _data)" data="{\\"requestId\\": $(decode_log.requestId), \\"_data\\": $(parse)}" ] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_large)}" ] submit_tx [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch -> parse -> encode_large -> encode_tx -> submit_tx """ ``` This is an example legacy v1 job spec for returning large responses in one Chainlink API Call. ```json { "name": "large-word", "initiators": [ { "id": 9, "jobSpecId": "7a97ff84-93ec-406d-9062-1b2531f9251a", "type": "runlog", "params": { "address": "0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8" } } ], "tasks": [ { "jobSpecId": "7a97ff8493ec406d90621b2531f9251a", "type": "httpget" }, { "jobSpecId": "7a97ff8493ec406d90621b2531f9251a", "type": "jsonparse" }, { "jobSpecId": "7a97ff8493ec406d90621b2531f9251a", "type": "resultcollect" }, { "jobSpecId": "7a97ff8493ec406d90621b2531f9251a", "type": "ethtx", "confirmations": 1, "params": { "abiEncoding": ["bytes32", "bytes"] } } ] } ``` --- # GET > Int256 Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-get-int256 This is an example v2 (TOML) job spec for calling any public API, retrieving a number , removing its decimals then returning *int256* in one Chainlink API Call. Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. ```toml type = "directrequest" schemaVersion = 1 name = "Get > Int256 - (TOML)" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"] parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"] multiply [type="multiply" input="$(parse)" times="$(decode_cbor.times)"] encode_data [type="ethabiencode" abi="(bytes32 requestId, int256 value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(multiply) }"] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}" ] submit_tx [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch -> parse -> multiply -> encode_data -> encode_tx -> submit_tx """ ``` --- # GET > String Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-get-string This is an example v2 (TOML) job spec for returning a *string* in one Chainlink API Call. Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. To test it from a smart contract, see this [Example](/any-api/get-request/examples/array-response). --- # GET > Uint256 Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/direct-request-get-uint256 This is an example v2 (TOML) job spec for calling any public API, retrieving a number , removing its decimals then returning *uint256* in one Chainlink API Call. Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. To test it from a smart contract, see this [Example](/any-api/get-request/examples/single-word-response). ```toml type = "directrequest" schemaVersion = 1 name = "Get > Uint256 - (TOML)" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"] parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"] multiply [type="multiply" input="$(parse)" times="$(decode_cbor.times)"] encode_data [type="ethabiencode" abi="(bytes32 requestId, uint256 value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(multiply) }"] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}" ] submit_tx [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch -> parse -> multiply -> encode_data -> encode_tx -> submit_tx """ ``` --- # MultiWord Example Job Spec Source: https://docs.chain.link/chainlink-nodes/job-specs/multi-word-job This is an example v2 (TOML) job spec for returning multiple responses in 1 Chainlink API Call.Note that the job calls the `fulfillOracleRequest2` function. If you are a node operator, use an [Operator contract](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/operatorforwarder/Operator.sol) with this job. To test it from a smart contract, see this [Example](/any-api/get-request/examples/multi-variable-responses). ```toml type = "directrequest" schemaVersion = 1 name = "multi-word (TOML)" maxTaskDuration = "0s" contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] decode_log -> decode_cbor decode_cbor -> btc decode_cbor -> usd decode_cbor -> eur btc [type="http" method=GET url="$(decode_cbor.urlBTC)" allowunrestrictednetworkaccess="true"] btc_parse [type="jsonparse" path="$(decode_cbor.pathBTC)" data="$(btc)"] btc_multiply [type="multiply" input="$(btc_parse)", times="100000"] btc -> btc_parse -> btc_multiply usd [type="http" method=GET url="$(decode_cbor.urlUSD)" allowunrestrictednetworkaccess="true"] usd_parse [type="jsonparse" path="$(decode_cbor.pathUSD)" data="$(usd)"] usd_multiply [type="multiply" input="$(usd_parse)", times="100000"] usd -> usd_parse -> usd_multiply eur [type="http" method=GET url="$(decode_cbor.urlEUR)" allowunrestrictednetworkaccess="true"] eur_parse [type="jsonparse" path="$(decode_cbor.pathEUR)" data="$(eur)"] eurs_multiply [type="multiply" input="$(eur_parse)", times="100000"] eur -> eur_parse -> eurs_multiply btc_multiply -> encode_mwr usd_multiply -> encode_mwr eurs_multiply -> encode_mwr // MWR API does NOT auto populate the requestID. encode_mwr [type="ethabiencode" abi="(bytes32 requestId, uint256 _btc, uint256 _usd, uint256 _eurs)" data="{\\"requestId\\": $(decode_log.requestId), \\"_btc\\": $(btc_multiply), \\"_usd\\": $(usd_multiply), \\"_eurs\\": $(eurs_multiply)}" ] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_mwr)}" ] submit_tx [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"] encode_mwr -> encode_tx -> submit_tx """ ``` --- # Job Types Source: https://docs.chain.link/chainlink-nodes/oracle-jobs/all-jobs This guide outlines different job types. ## Solidity cron jobs Executes a job on a schedule. Does not rely on any kind of external trigger. ### Spec format ```toml type = "cron" schemaVersion = 1 evmChainID = 1 schedule = "CRON_TZ=UTC * */20 * * * *" externalJobID = "0EEC7E1D-D0D2-476C-A1A8-72DFB6633F01" observationSource = """ fetch [type="http" method=GET url="https://chain.link/ETH-USD"] parse [type="jsonparse" path="data,price"] multiply [type="multiply" times=100] fetch -> parse -> multiply """ ``` ### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). ### Unique fields - `schedule`: the frequency with which the job is to be run. There are two ways to specify this: - Traditional UNIX cron format, but with 6 fields, not 5. The extra field allows for "seconds" granularity. **Note:** you *must* specify the `CRON_TZ=...` parameter if you use this format. - `@` shorthand, e.g. `@every 1h`. This shorthand does not take account of the node's timezone, rather, it simply begins counting down the moment that the job is added to the node (or the node is rebooted). As such, no `CRON_TZ` parameter is needed. For all supported schedules, please refer to the [cron library documentation](https://pkg.go.dev/github.com/robfig/cron?utm_source=godoc). ### Job type specific pipeline variables - `$(jobSpec.databaseID)`: the ID of the job spec in the local database. You shouldn't need this in 99% of cases. - `$(jobSpec.externalJobID)`: the globally-unique job ID for this job. Used to coordinate between node operators in certain cases. - `$(jobSpec.name)`: the local name of the job. - `$(jobRun.meta)`: a map of metadata that can be sent to a bridge, etc. ## Direct request jobs Executes a job upon receipt of an explicit request made by a user. The request is detected via a log emitted by an Oracle or Operator contract. This is similar to the legacy ethlog/runlog style of jobs. ### Spec format ```toml type = "directrequest" schemaVersion = 1 evmChainID = 1 name = "example eth request event spec" contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" # Optional fields: # requesters = [ # "0xAaAA1F8ee20f5565510b84f9353F1E333e753B7a", # "0xBbBb70f0E81c6F3430dfDc9fa02fB22bDD818c4E" # ] # minContractPaymentLinkJuels = "100000000000000" # externalJobID = "0EEC7E1D-D0D2-476C-A1A8-72DFB6633F02" # minIncomingConfirmations = 10 observationSource = """ ds [type="http" method=GET url="http://example.com"] ds_parse [type="jsonparse" path="USD"] ds_multiply [type="multiply" times=100] ds -> ds_parse -> ds_multiply """ ``` #### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). #### Unique fields - `contractAddress`: The Oracle or Operator contract to monitor for requests - `requesters`: Optional - Allows whitelisting requesters - `minContractPaymentLinkJuels` Optional - Allows you to specify a job-specific minimum contract payment - `minIncomingConfirmations` Optional - Allows you to specify a job-specific `MIN_INCOMING_CONFIRMATIONS` value, must be greater than global `MIN_INCOMING_CONFIRMATIONS` #### Job type specific pipeline variables - `$(jobSpec.databaseID)`: the ID of the job spec in the local database. You shouldn't need this in 99% of cases. - `$(jobSpec.externalJobID)`: the globally-unique job ID for this job. Used to coordinate between node operators in certain cases. - `$(jobSpec.name)`: the local name of the job. - `$(jobRun.meta)`: a map of metadata that can be sent to a bridge, etc. - `$(jobRun.logBlockHash)`: the block hash in which the initiating log was received. - `$(jobRun.logBlockNumber)`: the block number in which the initiating log was received. - `$(jobRun.logTxHash)`: the transaction hash that generated the initiating log. - `$(jobRun.logAddress)`: the address of the contract to which the initiating transaction was sent. - `$(jobRun.logTopics)`: the log's topics (`indexed` fields). - `$(jobRun.logData)`: the log's data (non-`indexed` fields). - `$(jobRun.blockReceiptsRoot)` : the root of the receipts trie of the block (hash). - `$(jobRun.blockTransactionsRoot)` : the root of the transaction trie of the block (hash). - `$(jobRun.blockStateRoot)` : the root of the final state trie of the block (hash). ### Examples #### Get > Uint256 job Let's assume that a user makes a request to an oracle to call a public API, retrieve a number from the response, remove any decimals and return *uint256*. - The smart contract example can be found [here](/any-api/get-request/examples/single-word-response). - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-get-uint256). #### Get > Int256 job Let's assume that a user makes a request to an oracle to call a public API, retrieve a number from the response, remove any decimals and return *int256*. - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-get-int256). #### Get > Bool job Let's assume that a user makes a request to an oracle to call a public API, retrieve a boolean from the response and return *bool*. - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-get-bool). #### Get > String job Let's assume that a user makes a request to an oracle and would like to fetch a *string* from the response. - The smart contract example can be found [here](/any-api/get-request/examples/array-response). - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-get-string). #### Get > Bytes job Let's assume that a user makes a request to an oracle and would like to fetch *bytes* from the response (meaning a response that contains an arbitrary-length raw byte data). - The smart contract example can be found [here](/any-api/get-request/examples/large-responses). - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-get-bytes). #### Multi-Word job Let's assume that a user makes a request to an oracle and would like to fetch multiple words in one single request. - The smart contract example can be found [here](/any-api/get-request/examples/multi-variable-responses). - The job spec example can be found [here](/chainlink-nodes/job-specs/multi-word-job). #### Existing job Using an *existing* Oracle Job makes your smart contract code more succinct. Let's assume that a user makes a request to an oracle that leverages [Etherscan External Adapter](https://github.com/smartcontractkit/external-adapters-js/tree/develop/packages/sources/etherscan) to retrieve the gas price. - The smart contract example can be found [here](/any-api/get-request/examples/existing-job-request). - The job spec example can be found [here](/chainlink-nodes/job-specs/direct-request-existing-job). ## Flux Monitor Jobs The Flux Monitor job type is for continually-updating data feeds that aggregate responses from multiple oracles. The oracles servicing the feed submit rounds based on several triggers: - An occasional poll, which must show that there has been sufficient deviation from an offchain data source before a new result is submitted - New rounds initiated by other oracles on the feeds. If another oracle notices sufficient deviation, all other oracles will submit their current observations as well. - A heartbeat, which ensures that even if no deviation occurs, we submit a new result to prove liveness. This can take one of two forms: - The "idle timer", which begins counting down each time a round is started - The "drumbeat", which simply ticks at a steady interval, much like a `cron` job ### Spec format ```toml type = "fluxmonitor" schemaVersion = 1 name = "example flux monitor spec" contractAddress = "0x3cCad4715152693fE3BC4460591e3D3Fbd071b42" externalJobID = "0EEC7E1D-D0D2-476C-A1A8-72DFB6633F03" threshold = 0.5 absoluteThreshold = 0.0 # optional idleTimerPeriod = "1s" idleTimerDisabled = false pollTimerPeriod = "1m" pollTimerDisabled = false drumbeatEnabled = true drumbeatSchedule = "CRON_TZ=UTC * */20 * * * *" observationSource = """ // data source 1 ds1 [type="http" method=GET url="https://pricesource1.com" requestData="{\\"coin\\": \\"ETH\\", \\"market\\": \\"USD\\"}"] ds1_parse [type="jsonparse" path="data,result"] // data source 2 ds2 [type="http" method=GET url="https://pricesource2.com" requestData="{\\"coin\\": \\"ETH\\", \\"market\\": \\"USD\\"}"] ds2_parse [type="jsonparse" path="data,result"] ds1 -> ds1_parse -> medianized_answer ds2 -> ds2_parse -> medianized_answer medianized_answer [type=median] """ ``` ### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). ### Unique fields - `contractAddress`: the address of the FluxAggregator contract that manages the feed. - `threshold`: the percentage threshold of deviation from the previous onchain answer that must be observed before a new set of observations are submitted to the contract. - `absoluteThreshold`: the absolute numerical deviation that must be observed from the previous onchain answer before a new set of observations are submitted to the contract. This is primarily useful with data that can legitimately sometimes hit 0, as it's impossible to calculate a percentage deviation from 0. - `idleTimerPeriod`: the amount of time (after the start of the last round) after which a new round will be automatically initiated, regardless of any observed offchain deviation. - `idleTimerDisabled`: whether the idle timer is used to trigger new rounds. - `drumbeatEnabled`: whether the drumbeat is used to trigger new rounds. - `drumbeatSchedule`: the cron schedule of the drumbeat. This field supports the same syntax as the cron job type (see the [cron library documentation](https://pkg.go.dev/github.com/robfig/cron?utm_source=godoc) for details). CRON_TZ is required. - `pollTimerPeriod`: the frequency with which the offchain data source is checked for deviation against the previously submitted onchain answer. - `pollTimerDisabled`: whether the occasional deviation check is used to trigger new rounds. - **Notes:** - For duration parameters, the maximum unit of time is `h` (hour). Durations of a day or longer must be expressed in hours. - If no time unit is provided, the default unit is nanoseconds, which is almost never what you want. ### Job type specific pipeline variables - `$(jobSpec.databaseID)`: the ID of the job spec in the local database. You shouldn't need this in 99% of cases. - `$(jobSpec.externalJobID)`: the globally-unique job ID for this job. Used to coordinate between node operators in certain cases. - `$(jobSpec.name)`: the local name of the job. - `$(jobRun.meta)`: a map of metadata that can be sent to a bridge, etc. ## Keeper jobs Keeper jobs occasionally poll a smart contract method that expresses whether something in the contract is ready for some onchain action to be performed. When it's ready, the job executes that onchain action. Examples: - Liquidations - Rebalancing portfolios - Rebase token supply adjustments - Auto-compounding - Limit orders ### Spec format ```toml type = "keeper" schemaVersion = 1 evmChainID = 1 name = "example keeper spec" contractAddress = "0x7b3EC232b08BD7b4b3305BE0C044D907B2DF960B" fromAddress = "0xa8037A20989AFcBC51798de9762b351D63ff462e" ``` ### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). ### Unique fields - `evmChainID`: The numeric chain ID of the chain on which Chainlink Automation Registry is deployed - `contractAddress`: The address of the Chainlink Automation Registry contract to poll and update - `fromAddress`: The Oracle node address from which to send updates - `externalJobID`: This is an optional field. When omitted it will be generated ## Offchain reporting jobs Offchain Reporting (OCR) jobs are used very similarly to Flux Monitor jobs. They update data feeds with aggregated data from many Chainlink oracle nodes. However, they do this aggregation using a cryptographically-secure offchain protocol that makes it possible for only a single node to submit all answers from all participating nodes during each round (with proofs that the other nodes' answers were legitimately provided by those nodes), which saves a significant amount of gas. Offchain reporting jobs require the `FEATURE_OFFCHAIN_REPORTING=true` environment variable. ### Bootstrap node Every OCR cluster requires at least one bootstrap node as a kind of "rallying point" that enables the other nodes to find one another. Bootstrap nodes do not participate in the aggregation protocol and do not submit answers to the feed. #### Spec format ```toml type = "offchainreporting" schemaVersion = 1 evmChainID = 1 contractAddress = "0x27548a32b9aD5D64c5945EaE9Da5337bc3169D15" p2pBootstrapPeers = [ "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", ] isBootstrapPeer = true externalJobID = "0EEC7E1D-D0D2-476C-A1A8-72DFB6633F05" ``` #### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). #### Unique fields - `contractAddress`: The address of the `OffchainReportingAggregator` contract. - `evmChainID`: The chain ID of the EVM chain in which the job will operate. - `p2pBootstrapPeers`: A list of libp2p dial addresses of the other bootstrap nodes helping oracle nodes find one another on the network. It is used with P2P networking stack V1 as follows: `p2pBootstrapPeers = [ "/dns4/HOST_NAME_OR_IP/tcp/PORT/p2p/BOOTSTRAP_NODE'S_P2P_ID" ]` - `p2pv2Bootstrappers`: A list of libp2p dial addresses of the other bootstrap nodes helping oracle nodes find one another on the network. It is used with P2P networking stack V2 as follows: `p2pv2Bootstrappers = [ "BOOTSTRAP_NODE'S_P2P_ID@HOST_NAME_OR_IP:PORT" ]` - `isBootstrapPeer`: This must be set to `true`. #### Job type specific pipeline variables - `$(jobSpec.databaseID)`: The ID of the job spec in the local database. You shouldn't need this in 99% of cases. - `$(jobSpec.externalJobID)`: The globally-unique job ID for this job. Used to coordinate between node operators in certain cases. - `$(jobSpec.name)`: The local name of the job. - `$(jobRun.meta)`: A map of metadata that can be sent to a bridge, etc. ### Oracle node Oracle nodes, on the other hand, are responsible for submitting answers. ### Spec format ```toml type = "offchainreporting" schemaVersion = 1 evmChainID = 1 name = "OCR: ETH/USD" contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C" externalJobID = "0EEC7E1D-D0D2-476C-A1A8-72DFB6633F06" p2pPeerID = "12D3KooWApUJaQB2saFjyEUfq6BmysnsSnhLnY5CF9tURYVKgoXK" p2pBootstrapPeers = [ "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", ] isBootstrapPeer = false keyBundleID = "7f993fb701b3410b1f6e8d4d93a7462754d24609b9b31a4fe64a0cb475a4d934" monitoringEndpoint = "chain.link:4321" transmitterAddress = "0xF67D0290337bca0847005C7ffD1BC75BA9AAE6e4" observationTimeout = "10s" blockchainTimeout = "20s" contractConfigTrackerSubscribeInterval = "2m" contractConfigTrackerPollInterval = "1m" contractConfigConfirmations = 3 observationSource = """ // data source 1 ds1 [type="bridge" name=eth_usd] ds1_parse [type="jsonparse" path="one,two"] ds1_multiply [type="multiply" times=100] // data source 2 ds2 [type="http" method=GET url="https://chain.link/eth_usd" requestData="{\\"hi\\": \\"hello\\"}"] ds2_parse [type="jsonparse" path="three,four"] ds2_multiply [type="multiply" times=100] ds1 -> ds1_parse -> ds1_multiply -> answer ds2 -> ds2_parse -> ds2_multiply -> answer answer [type=median] """ ``` #### Shared fields See [shared fields](/chainlink-nodes/oracle-jobs/jobs/#shared-fields). #### Unique fields - `contractAddress`: The address of the `OffchainReportingAggregator` contract. - `evmChainID`: The chain ID of the EVM chain in which the job will operate. - `p2pPeerID`: The base58-encoded libp2p public key of this node. - `p2pBootstrapPeers`: A list of libp2p dial addresses of the other bootstrap nodes helping oracle nodes find one another on the network. It is used with P2P networking stack V1 as follows: `p2pBootstrapPeers = [ "/dns4//tcp//p2p/" ]` - `p2pv2Bootstrappers`: A list of libp2p dial addresses of the other bootstrap nodes helping oracle nodes find one another on the network. It is used with P2P networking stack V2 as follows: `p2pv2Bootstrappers = [ "@:" ]` - `keyBundleID`: The hash of the OCR key bundle to be used by this node. The Chainlink node keystore manages these key bundles. Use the node **Key Management** UI or the `chainlink keys ocr` sub-commands in the CLI to create and manage key bundles. - `monitoringEndpoint`: The URL of the telemetry endpoint to send OCR metrics to. - `transmitterAddress`: The Ethereum address from which to send aggregated submissions to the OCR contract. - `observationTimeout`: The maximum duration to wait before an offchain request for data is considered to be failed/unfulfillable. - `blockchainTimeout`: The maximum duration to wait before an onchain request for data is considered to be failed/unfulfillable. - `contractConfigTrackerSubscribeInterval`: The interval at which to retry subscribing to onchain config changes if a subscription has not yet successfully been made. - `contractConfigTrackerPollInterval`: The interval at which to proactively poll the onchain config for changes. - `contractConfigConfirmations`: The number of blocks to wait after an onchain config change before considering it worthy of acting upon. #### Job type specific pipeline variables - `$(jobSpec.databaseID)`: The ID of the job spec in the local database. You shouldn't need this in 99% of cases. - `$(jobSpec.externalJobID)`: The globally-unique job ID for this job. Used to coordinate between node operators in certain cases. - `$(jobSpec.name)`: The local name of the job. - `$(jobRun.meta)`: A map of metadata that can be sent to a bridge, etc. ## Webhook Jobs Webhook jobs can be initiated by HTTP request, either by a user or external initiator.