diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index be687f4..59cc931 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -1,13 +1,13 @@
# Hardhat Chainlink Plugin Documentation
-This document provides detailed information about each service module and its related methods available in the Hardhat Chainlink plugin.
+This document provides detailed information about each Chainlink service module and its related methods available in the Hardhat Chainlink plugin.
+To use these methods please ensure that your Hardhat environment is configured according to the [README](README.md).
* [Hardhat Chainlink Plugin Documentation](#hardhat-chainlink-plugin-documentation)
- * [Data Feed Services](#data-feed-services)
+ * [Data Feeds Services](#data-feeds-services)
* [Service alias: `dataFeed`](#service-alias-datafeed)
- * [Methods](#methods)
- * [Get Latest RoundAnswer](#get-latest-roundanswer)
+ * [Get Latest Round Answer](#get-latest-round-answer)
* [Get Round Answer](#get-round-answer)
* [Get Latest Round Data](#get-latest-round-data)
* [Get Round Data](#get-round-data)
@@ -16,8 +16,7 @@ This document provides detailed information about each service module and its re
* [Get Description](#get-description)
* [Get Version](#get-version)
* [Service alias: `dataFeedProxy`](#service-alias-datafeedproxy)
- * [Methods](#methods-1)
- * [Get Latest Round Answer](#get-latest-round-answer)
+ * [Get Latest Round Answer](#get-latest-round-answer-1)
* [Get Round Answer](#get-round-answer-1)
* [Get Latest Round Data](#get-latest-round-data-1)
* [Get Round Data](#get-round-data-1)
@@ -29,11 +28,10 @@ This document provides detailed information about each service module and its re
* [Get PhaseId](#get-phaseid)
* [Get Phase Aggregators](#get-phase-aggregators)
* [Service alias: `feedRegistry`](#service-alias-feedregistry)
- * [Methods](#methods-2)
* [Get Latest Round Data](#get-latest-round-data-2)
* [Get Round Data](#get-round-data-2)
- * [Get Proposed Latest Round Data](#get-proposed-latest-round-data)
- * [Get Proposed Round Data](#get-proposed-round-data)
+ * [Get Latest Round Data of Proposed Feed](#get-latest-round-data-of-proposed-feed)
+ * [Get Round Data of Proposed Feed](#get-round-data-of-proposed-feed)
* [Get Round Feed](#get-round-feed)
* [Get Feed](#get-feed)
* [Get Proposed Feed](#get-proposed-feed)
@@ -48,16 +46,13 @@ This document provides detailed information about each service module and its re
* [Get Phase Range](#get-phase-range)
* [Get Current Phase ID](#get-current-phase-id)
* [Service alias: `l2Sequencer`](#service-alias-l2sequencer)
- * [Methods](#methods-3)
* [Is L2 Sequencer Up](#is-l2-sequencer-up)
* [Get Time Since L2 Sequencer Is Up](#get-time-since-l2-sequencer-is-up)
* [Service alias: `ens`](#service-alias-ens)
- * [Methods](#methods-4)
* [Resolve Aggregator Address](#resolve-aggregator-address)
* [Resolve Aggregator Address With Subdomains](#resolve-aggregator-address-with-subdomains)
* [VRF Service](#vrf-service)
* [Service alias: `vrf`](#service-alias-vrf)
- * [Methods](#methods-5)
* [Create Subscription](#create-subscription)
* [Fund Subscription](#fund-subscription)
* [Cancel Subscription](#cancel-subscription)
@@ -78,14 +73,12 @@ This document provides detailed information about each service module and its re
* [Get Type and Version](#get-type-and-version)
* [Automation Services](#automation-services)
* [Service alias: `automationRegistrar`](#service-alias-automationregistrar)
- * [Methods](#methods-6)
* [Register Upkeep](#register-upkeep)
* [Get Pending Request](#get-pending-request)
* [Cancel Request](#cancel-request)
* [Get Registration Config](#get-registration-config)
* [Get Type And Version](#get-type-and-version-1)
* [Service alias: `automationRegistry`](#service-alias-automationregistry)
- * [Methods](#methods-7)
* [Get State](#get-state)
* [Get Active Upkeep IDs](#get-active-upkeep-ids)
* [Get Max Payment For Gas](#get-max-payment-for-gas)
@@ -99,376 +92,767 @@ This document provides detailed information about each service module and its re
* [Get Keeper Info](#get-keeper-info)
* [Get Type And Version](#get-type-and-version-2)
* [Get Upkeep Transcoder Version](#get-upkeep-transcoder-version)
+ * [Functions Service](#functions-service)
+ * [Service alias: `functions`](#service-alias-functions)
+ * [Create subscription](#create-subscription-1)
+ * [Fund subscription](#fund-subscription-1)
+ * [Cancel subscription](#cancel-subscription-1)
+ * [Get subscription details](#get-subscription-details-1)
+ * [Request subscription owner transfer](#request-subscription-owner-transfer-1)
+ * [Accept subscription owner transfer](#accept-subscription-owner-transfer-1)
+ * [Add subscription consumer](#add-subscription-consumer)
+ * [Remove subscription consumer](#remove-subscription-consumer)
+ * [Timeout requests](#timeout-requests)
+ * [Task/Subtask/CLI](#tasksubtaskcli)
+ * [HRE](#hre)
+ * [Estimate the cost of a request](#estimate-the-cost-of-a-request)
+ * [Initialize SubscriptionManager class](#initialize-subscriptionmanager-class)
+ * [Initialize ResponseListener class](#initialize-responselistener-class)
+ * [Initialize SecretsManager class](#initialize-secretsmanager-class)
+ * [Listen for a response for single Functions request](#listen-for-a-response-for-single-functions-request)
+ * [Listen for a response for single Functions request (from transaction)](#listen-for-a-response-for-single-functions-request-from-transaction)
+ * [Listen for responses](#listen-for-responses)
+ * [Stop response listener](#stop-response-listener)
+ * [Fetch DON public keys](#fetch-don-public-keys)
+ * [Encrypt Off-Chain secrets URLs](#encrypt-off-chain-secrets-urls)
+ * [Verify Off-Chain secrets URLs](#verify-off-chain-secrets-urls)
+ * [Encrypt secrets](#encrypt-secrets)
+ * [Upload encrypted secrets to DON](#upload-encrypted-secrets-to-don)
+ * [List DON hosted encrypted secrets](#list-don-hosted-encrypted-secrets)
+ * [Build DON hosted encrypted secrets reference](#build-don-hosted-encrypted-secrets-reference)
+ * [Fetch Request Commitments](#fetch-request-commitments)
* [Utilities](#utilities)
* [Service alias: `utils`](#service-alias-utils)
- * [Methods](#methods-8)
* [Get Data Feed Proxy/Registry Round ID](#get-data-feed-proxyregistry-round-id)
* [Parse Data Feed Proxy/Registry Round ID](#parse-data-feed-proxyregistry-round-id)
* [Transfer ETH](#transfer-eth)
- * [Sandbox `sandbox`](#sandbox-sandbox)
- * [Service alias: `node`](#service-alias-node)
- * [Methods](#methods-9)
- * [Run Chainlink node](#run-chainlink-node)
- * [Restart Chainlink node](#restart-chainlink-node)
- * [Stop Chainlink node](#stop-chainlink-node)
- * [Get ETH keys](#get-eth-keys)
- * [Get P2P keys](#get-p2p-keys)
- * [Get OCR keys](#get-ocr-keys)
- * [Get jobs](#get-jobs)
- * [Create Direct Request job](#create-direct-request-job)
- * [Service alias: `linkToken`](#service-alias-linktoken)
- * [Methods](#methods-10)
- * [Deploy contract](#deploy-contract)
- * [Transfer](#transfer)
- * [Get allowance](#get-allowance)
- * [Increase approval](#increase-approval)
- * [Decrease approval](#decrease-approval)
- * [Service alias: `operator`](#service-alias-operator)
- * [Methods](#methods-11)
- * [Deploy contract](#deploy-contract-1)
- * [Set authorized sender](#set-authorized-sender)
- * [Service alias: `drConsumer`](#service-alias-drconsumer)
- * [Methods](#methods-12)
- * [Deploy contract](#deploy-contract-2)
- * [Request data](#request-data)
- * [Get latest answer](#get-latest-answer)
+ * [Create GitHub gist](#create-github-gist)
+ * [Delete GitHub gist](#delete-github-gist)
+ * [Build CBOR for Functions request](#build-cbor-for-functions-request)
+ * [Decode HEX String](#decode-hex-string)
-## Data Feed Services
+## Data Feeds Services
Chainlink [Data Feeds](https://docs.chain.link/data-feeds) are decentralized oracles that provide reliable off-chain data to smart contracts on the blockchain.
Using this service, developers can access the latest round answer and other relevant information from the Data Feeds,
enabling them to fetch real-world data in their web3 projects.
-### Service alias: `dataFeed`
-
+### Service alias: [`dataFeed`](src%2Ffeeds%2FdataFeed.ts)
+```typescript
+const dataFeed = hre.chainlink.dataFeed;
+```
This section provides methods and functionalities designed to interact with the OffchainAggregator smart contract,
which serves as the core component of Chainlink Data Feeds.
-### Methods
-
-#### Get Latest RoundAnswer
+#### Get Latest Round Answer
- **Method:** getLatestRoundAnswer
-- **Description:** Get the latest round answer for a data feed
-- **Arguments:**
+- **Description:** Get the latest round answer for a Data Feed
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
+- **Returns:** `(roundAnswer: BigNumber)`
+ - `roundAnswer`: Latest round answer for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundAnswer = await dataFeed.getLatestRoundAnswer(dataFeedAddress);
+ ```
#### Get Round Answer
- **Method:** getRoundAnswer
-- **Description:** Get the round answer for a data feed
-- **Arguments:**
+- **Description:** Get the round answer for a Data Feed
+- **Arguments:** `(dataFeedAddress: string, roundId: BigNumberish)`
- `dataFeedAddress`: Address of Data Feed
- `roundId`: Round ID of Data Feed
+- **Returns:** `(roundAnswer: BigNumber)`
+ - `roundAnswer`: Round answer for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = 1;
+ const roundAnswer = await dataFeed.getRoundAnswer(dataFeedAddress, roundId);
+ ```
#### Get Latest Round Data
- **Method:** getLatestRoundData
-- **Description:** Get the latest round data for a data feed
-- **Arguments:**
+- **Description:** Get the latest round data for a Data Feed
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundData = await dataFeed.getLatestRoundData(dataFeedAddress);
+ ```
#### Get Round Data
- **Method:** getRoundData
-- **Description:** Get the round data for a data feed
-- **Arguments:**
+- **Description:** Get the round data for a Data Feed
+- **Arguments:** `(dataFeedAddress: string, roundId: BigNumberish)`
- `dataFeedAddress`: Address of Data Feed
- `roundId`: Round ID of Data Feed
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = 1;
+ const roundData = await dataFeed.getRoundData(dataFeedAddress, roundId);
+ ```
#### Get LatestRoundId
- **Method:** getLatestRoundId
- **Description:** Get the latest round ID
-- **Arguments:**
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
- `roundId`: Round ID of Data Feed
+- **Returns:** `(roundId: BigNumber)`
+ - `roundId`: Latest round ID of a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = await dataFeed.getLatestRoundId(dataFeedAddress);
+ ```
#### Get Decimals
- **Method:** getDecimals
-- **Description:** Get the decimals for a data feed
-- **Arguments:**
+- **Description:** Get the decimals for a Data Feed
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
+- **Returns:** `(decimals: number)`
+ - `decimals`: Decimals for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const decimals = await dataFeed.getDecimals(dataFeedAddress);
+ ```
#### Get Description
- **Method:** getDescription
-- **Description:** Get the description for a data feed
-- **Arguments:**
+- **Description:** Get the description for a Data Feed
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
+- **Returns:** `(description: string)`
+ - `description`: Description for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const description = await dataFeed.getDescription(dataFeedAddress);
+ ```
#### Get Version
- **Method:** getAggregatorVersion
-- **Description:** Get the version for a data feed
-- **Arguments:**
+- **Description:** Get the version for a Data Feed
+- **Arguments:** `(dataFeedAddress: string)`
- `dataFeedAddress`: Address of Data Feed
-
-
-### Service alias: `dataFeedProxy`
-
+- **Returns:** `(version: BigNumber)`
+ - `version`: Version for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const version = await dataFeed.getVersion(dataFeedAddress);
+ ```
+
+### Service alias: [`dataFeedProxy`](src%2Ffeeds%2FdataFeedProxy.ts)
+```typescript
+const dataFeedProxy = hre.chainlink.dataFeedProxy;
+```
This section provides methods and functionalities designed to interact with the EACAggregatorProxy smart contract,
which acts as a proxy for Chainlink Data Feeds.
-### Methods
-
#### Get Latest Round Answer
- **Method:** getLatestRoundAnswer
-- **Description:** Get the latest round answer for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get the latest round answer for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(roundAnswer: BigNumber)`
+ - `roundAnswer`: Latest round answer for a current Data Feed via proxy
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundAnswer = await dataFeedProxy.getLatestRoundAnswer(dataFeedProxyAddress);
+ ```
#### Get Round Answer
- **Method:** getRoundAnswer
-- **Description:** Get round answer for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get round answer for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string, roundId: BigNumberish)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
- `roundId`: Round ID of Data Feed Proxy
+- **Returns:** `(roundAnswer: BigNumber)`
+ - `roundAnswer`: Round answer for a current Data Feed via proxy
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = 1;
+ const roundAnswer = await dataFeedProxy.getRoundAnswer(dataFeedProxyAddress, roundId);
+ ```
#### Get Latest Round Data
- **Method:** getLatestRoundData
-- **Description:** Get the latest round data for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get the latest round data for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed Proxy
+ - `answer`: Round answer for current Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundData = await dataFeedProxy.getLatestRoundData(dataFeedProxyAddress);
+ ```
#### Get Round Data
- **Method:** getRoundData
-- **Description:** Get round data for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get round data for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string, roundId: BigNumberish)`
- `dataFeedProxyAddress`: Address of Data Feed
- `roundId`: Round ID of Data Feed Proxy
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed Proxy
+ - `answer`: Round answer for current Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = 1;
+ const roundData = await dataFeedProxy.getRoundData(dataFeedProxyAddress, roundId);
+ ```
#### Get Latest RoundId
- **Method:** getLatestRoundId
- **Description:** Get the latest round ID of a proxy
-- **Arguments:**
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
- - `roundId`: Round ID of Data Feed Proxy
+- **Returns:** `(roundId: BigNumber)`
+ - `roundId`: Latest round ID of a Data Feed Proxy
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const roundId = await dataFeedProxy.getLatestRoundId(dataFeedProxyAddress);
+ ```
#### Get Decimals
- **Method:** getDecimals
-- **Description:** Get decimals for a data feed
-- **Arguments:**
+- **Description:** Get decimals for current Data Feed
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(decimals: number)`
+ - `decimals`: Decimals for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const decimals = await dataFeedProxy.getDecimals(dataFeedProxyAddress);
+ ```
#### Get Description
- **Method:** getDescription
-- **Description:** Get description for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get description for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(description: string)`
+ - `description`: Description for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const description = await dataFeedProxy.getDescription(dataFeedProxyAddress);
+ ```
#### Get Version
- **Method:** getVersion
-- **Description:** Get version for a current data feed via proxy
-- **Arguments:**
+- **Description:** Get version for a current Data Feed via proxy
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(version: BigNumber)`
+ - `version`: Version for a Data Feed
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const version = await dataFeedProxy.getVersion(dataFeedProxyAddress);
+ ```
#### Get Aggregator
- **Method:** getAggregator
-- **Description:** Get current data feed address via proxy
-- **Arguments:**
+- **Description:** Get current Data Feed address via proxy
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of current Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const aggregatorAddress = await dataFeedProxy.getAggregator(dataFeedProxyAddress);
+ ```
#### Get PhaseId
- **Method:** getPhaseId
- **Description:** Get current phase ID of a proxy
-- **Arguments:**
+- **Arguments:** `(dataFeedProxyAddress: string)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
+- **Returns:** `(phaseId: BigNumber)`
+ - `phaseId`: Current phase ID of a Data Feed proxy
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const phaseId = await dataFeedProxy.getPhaseId(dataFeedProxyAddress);
+ ```
#### Get Phase Aggregators
- **Method:** getPhaseAggregators
-- **Description:** Get data feed address related to a phase ID of a proxy
-- **Arguments:**
+- **Description:** Get Data Feed address related to a phase ID of a proxy
+- **Arguments:** `(dataFeedProxyAddress: string, phaseId: BigNumberish)`
- `dataFeedProxyAddress`: Address of Data Feed Proxy
- `phaseId`: Phase ID of Data Feed Proxy
-
-
-### Service alias: `feedRegistry`
-
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator related to a phase ID of a proxy
+- **Usage:**
+ ```typescript
+ const dataFeedProxyAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const phaseId = 1;
+ const aggregatorAddress = await dataFeedProxy.getPhaseAggregators(dataFeedProxyAddress, phaseId);
+ ```
+
+### Service alias: [`feedRegistry`](src%2Ffeeds%2FfeedRegistry.ts)
+```typescript
+const feedRegistry = hre.chainlink.feedRegistry;
+```
This section provides methods and functionalities designed to interact with the [Feed Registry](https://docs.chain.link/data-feeds/feed-registry) smart contracts,
which acts is an on-chain mapping of assets to Data Feeds.
> **Note**
> Chainlink Feed Registry is exclusively available on the Ethereum mainnet.
-### Methods
-
#### Get Latest Round Data
- **Method:** `getLatestRoundData`
-- **Description:** Get the latest round data for a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get the latest round data for a specific Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundData = await feedRegistry.getLatestRoundData(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Get Round Data
- **Method:** `getRoundData`
-- **Description:** Get the round data for a specific data feed from the Feed Registry based on the provided round ID
-- **Arguments:**
+- **Description:** Get the round data for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, roundId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `roundId`: Feed Registry Round ID
-
-#### Get Proposed Latest Round Data
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundId = 1;
+ const roundData = await feedRegistry.getRoundData(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, roundId);
+ ```
+
+#### Get Latest Round Data of Proposed Feed
- **Method:** `proposedGetLatestRoundData`
-- **Description:** Get the latest round data (proposed) for a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get the latest round data for a proposed Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
-
-#### Get Proposed Round Data
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundData = await feedRegistry.proposedGetLatestRoundData(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
+
+#### Get Round Data of Proposed Feed
- **Method:** `proposedGetRoundData`
-- **Description:** Get the round data (proposed) for a specific data feed from the Feed Registry based on the provided round ID
-- **Arguments:**
+- **Description:** Get the round data for a proposed Data Feed from the Feed Registry based on the provided round ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, roundId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `roundId`: Data Feed Round ID
+- **Returns:** `(roundData: {
+ roundId: BigNumber;
+ answer: BigNumber;
+ startedAt: BigNumber;
+ updatedAt: BigNumber;
+ answeredInRound: BigNumber;
+})`
+ - `roundId`: Round ID of Data Feed
+ - `answer`: Round answer for a Data Feed
+ - `startedAt`: Timestamp of when the round started
+ - `updatedAt`: Timestamp of when the round was updated
+ - `answeredInRound`: Round ID of the round in which the answer was computed
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundId = 1;
+ const roundData = await feedRegistry.proposedGetRoundData(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, roundId);
+ ```
#### Get Round Feed
- **Method:** `getRoundFeed`
-- **Description:** Get the data feed address for a specific data feed from the Feed Registry based on the provided round ID
-- **Arguments:**
+- **Description:** Get the Data Feed address for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, roundId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `roundId`: Feed Registry Round ID
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundId = 1;
+ const aggregatorAddress = await feedRegistry.getRoundFeed(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, roundId);
+ ```
#### Get Feed
- **Method:** `getFeed`
-- **Description:** Get current data feed address for a specific token pair from the Feed Registry
-- **Arguments:**
+- **Description:** Get current Data Feed address for a specific token pair from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const aggregatorAddress = await feedRegistry.getFeed(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Get Proposed Feed
- **Method:** `getProposedFeed`
-- **Description:** Get the proposed data feed address for a specific token pair from the Feed Registry
-- **Arguments:**
+- **Description:** Get the proposed Data Feed address for a specific token pair from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const aggregatorAddress = await feedRegistry.getProposedFeed(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Is Feed Enabled
- **Method:** `isFeedEnabled`
-- **Description:** Check if a specific data feed is enabled in the Feed Registry
-- **Arguments:**
+- **Description:** Check if a specific Data Feed is enabled in the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, aggregatorAddress: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `aggregatorAddress`: Address of a Data Feed Offchain Aggregator
+- **Returns:** `(isEnabled: boolean)`
+ - `isEnabled`: Boolean value indicating if the Data Feed is enabled in the Feed Registry
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const aggregatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const isEnabled = await feedRegistry.isFeedEnabled(feedRegistryAddress, aggregatorAddress);
+ ```
#### Get Previous Round ID
- **Method:** `getPreviousRoundId`
-- **Description:** Get previous round ID for a specific data feed from the Feed Registry based on the provided round ID
-- **Arguments:**
+- **Description:** Get previous round ID for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, roundId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `roundId`: Feed Registry Round ID
+- **Returns:** `(roundId: BigNumber)`
+ - `roundId`: Previous round ID for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundId = 1;
+ const previousRoundId = await feedRegistry.getPreviousRoundId(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, roundId);
+ ```
#### Get Next Round ID
- **Method:** `getNextRoundId`
-- **Description:** Get next round ID for a specific data feed from the Feed Registry based on the provided round ID
-- **Arguments:**
+- **Description:** Get next round ID for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, roundId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `roundId`: Feed Registry Round ID
+- **Returns:** `(roundId: BigNumber)`
+ - `roundId`: Next round ID for a specific Data Feed from the Feed Registry based on the provided round ID
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const roundId = 1;
+ const nextRoundId = await feedRegistry.getNextRoundId(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, roundId);
+ ```
#### Get Decimals
- **Method:** `getDecimals`
-- **Description:** Get the decimals of a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get the decimals of a specific Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(decimals: number)`
+ - `decimals`: Decimals of a specific Data Feed from the Feed Registry
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const decimals = await feedRegistry.getDecimals(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Get Description
- **Method:** `getDescription`
-- **Description:** Get the description of a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get the description of a specific Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(description: string)`
+ - `description`: Description of a specific Data Feed from the Feed Registry
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const description = await feedRegistry.getDescription(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Get Version
- **Method:** `getVersion`
-- **Description:** Get the version of a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get the version of a specific Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
+- **Returns:** `(version: BigNumber)`
+ - `version`: Version of a specific Data Feed from the Feed Registry
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const version = await feedRegistry.getVersion(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
#### Get Phase
- **Method:** `getPhase`
-- **Description:** Get phase data for a specific data feed from the Feed Registry based on the provided phase ID
-- **Arguments:**
+- **Description:** Get phase data for a specific Data Feed from the Feed Registry based on the provided phase ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, phaseId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `phaseId`: Feed Registry Phase ID
+- **Returns:** `(phaseData: {
+ phaseId: BigNumber;
+ startingAggregatorRoundId: BigNumber;
+ endingAggregatorRoundId: BigNumber;
+})`
+ - `phaseId`: Feed Registry Phase ID
+ - `startingAggregatorRoundId`: Starting round ID of the phase
+ - `endingAggregatorRoundId`: Ending round ID of the phase
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const phaseId = 1;
+ const phaseData = await feedRegistry.getPhase(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, phaseId);
+ ```
#### Get Phase Feed
- **Method:** `getPhaseFeed`
-- **Description:** Get data feed address for a specific token pair from the Feed Registry based on the provided phase ID
-- **Arguments:**
+- **Description:** Get Data Feed address for a specific token pair from the Feed Registry based on the provided phase ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, phaseId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `phaseId`: Feed Registry Phase ID
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const phaseId = 1;
+ const aggregatorAddress = await feedRegistry.getPhaseFeed(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, phaseId);
+ ```
#### Get Phase Range
- **Method:** `getPhaseRange`
-- **Description:** Get the phase range data for a specific data feed from the Feed Registry based on the provided phase ID
-- **Arguments:**
+- **Description:** Get the phase range data for a specific Data Feed from the Feed Registry based on the provided phase ID
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string, phaseId: BigNumberish)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
- `phaseId`: Feed Registry Phase ID
+- **Returns:** `(phaseRangeData: {
+ startingRoundId: BigNumber;
+ endingRoundId: BigNumber;
+})`
+ - `startingRoundId`: Starting round ID of the phase
+ - `endingRoundId`: Ending round ID of the phase
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const phaseId = 1;
+ const phaseRangeData = await feedRegistry.getPhaseRange(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick, phaseId);
+ ```
#### Get Current Phase ID
- **Method:** `getCurrentPhaseId`
-- **Description:** Get current phase ID for a specific data feed from the Feed Registry
-- **Arguments:**
+- **Description:** Get current phase ID for a specific Data Feed from the Feed Registry
+- **Arguments:** `(feedRegistryAddress: string, feedRegistryBaseTick: string, feedRegistryQuoteTick: string)`
- `feedRegistryAddress`: Address of Feed Registry
- `feedRegistryBaseTick`: Address or denomination of the base tick in a token pair
- `feedRegistryQuoteTick`: Address or denomination of the quote tick in a token pair
-
-
-### Service alias: `l2Sequencer`
-
+- **Returns:** `(phaseId: BigNumber)`
+ - `phaseId`: Current phase ID for a specific Data Feed from the Feed Registry
+- **Usage:**
+ ```typescript
+ const feedRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const feedRegistryBaseTick = 'ETH';
+ const feedRegistryQuoteTick = 'USD';
+ const phaseId = await feedRegistry.getCurrentPhaseId(feedRegistryAddress, feedRegistryBaseTick, feedRegistryQuoteTick);
+ ```
+
+### Service alias: [`l2Sequencer`](src%2Ffeeds%2Fl2FeedUptimeSequencer.ts)
+```typescript
+const l2Sequencer = hre.chainlink.l2Sequencer;
+```
This section provides methods and functionalities designed to interact with the L2 Sequencer Uptime Feeds,
which acts as data feeds that track the availability status of the sequencer in Layer 2 (L2) networks, specifically in optimistic rollup protocols.
@@ -477,224 +861,439 @@ which acts as data feeds that track the availability status of the sequencer in
> including Arbitrum mainnet, Arbitrum Goerli testnet, Optimism mainnet,
> Optimism Goerli testnet, and Metis mainnet (Andromeda)
-### Methods
-
#### Is L2 Sequencer Up
- **Method:** `isL2SequencerUp`
- **Description:** Checks the availability status of the Layer 2 (L2) Sequencer using the provided address of the Layer 2 Sequencer Uptime Status Feed
-- **Arguments:**
+- **Arguments:** `(l2SequencerAddress: string)`
- `l2SequencerAddress`: Address of the Layer 2 Sequencer Uptime Status Feed
+- **Returns:** `(isUp: boolean)`
+ - `isUp`: Boolean value indicating if the Layer 2 Sequencer is up and operational
+- **Usage:**
+ ```typescript
+ const l2SequencerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const isUp = await l2Sequencer.isL2SequencerUp(l2SequencerAddress);
+ ```
#### Get Time Since L2 Sequencer Is Up
- **Method:** `getTimeSinceL2SequencerIsUp`
- **Description:** Retrieves the time elapsed since the Layer 2 (L2) Sequencer has been up and operational, based on the provided address of the Layer 2 Sequencer Uptime Status Feed
-- **Arguments:**
+- **Arguments:** `(l2SequencerAddress: string, gracePeriodTime: BigNumberish)`
- `l2SequencerAddress`: Address of the Layer 2 Sequencer Uptime Status Feed
- `gracePeriodTime`: Grace period duration in which the Sequencer is allowed to not return results
-
-### Service alias: `ens`
-
+- **Returns:** `(l2SequencerData: {
+ isSequencerUp: boolean;
+ timeSinceUp: BigNumber;
+ isGracePeriodOver: boolean;
+})`
+ - `isSequencerUp`: Boolean value indicating if the Layer 2 Sequencer is up and operational
+ - `timeSinceUp`: Time elapsed since the Layer 2 Sequencer has been up and operational
+ - `isGracePeriodOver`: Boolean value indicating if the grace period is over
+- **Usage:**
+ ```typescript
+ const l2SequencerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const gracePeriodTime = 3600;
+ const l2SequencerData = await l2Sequencer.getTimeSinceL2SequencerIsUp(l2SequencerAddress, gracePeriodTime);
+ ```
+
+### Service alias: [`ens`](src%2Ffeeds%2FensFeedsResolver.ts)
+```typescript
+const ens = hre.chainlink.ens;
+```
This section provides methods and functionalities designed to interact with the [Chainlink ENS Resolver](https://docs.chain.link/data-feeds/ens).
> **Note**
> Chainlink ENS is exclusively available on the Ethereum mainnet.
-### Methods
-
#### Resolve Aggregator Address
- **Method:** `resolveAggregatorAddress`
- **Description:** Resolve Data Feed address for a token pair using the Chainlink ENS Resolver
-- **Arguments:**
+- **Arguments:** `(baseTick: string, quoteTick: string)`
- `baseTick`: Base tick of the token pair
- `quoteTick`: Quote tick of the token pair
+- **Returns:** `(aggregatorAddress: string)`
+ - `aggregatorAddress`: Address of Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const baseTick = 'ETH';
+ const quoteTick = 'USD';
+ const aggregatorAddress = await ens.resolveAggregatorAddress(baseTick, quoteTick);
+ ```
#### Resolve Aggregator Address With Subdomains
- **Method:** `resolveAggregatorAddressWithSubdomains`
- **Description:** Resolve Data Feed address for a token pair using the Chainlink ENS Resolver with subdomains
-- **Arguments:**
- - `baseTick`: Base tick of the token pair.
- - `quoteTick`: Quote tick of the token pair.
+- **Arguments:** `(baseTick: string, quoteTick: string)`
+ - `baseTick`: Base tick of the token pair
+ - `quoteTick`: Quote tick of the token pair
+- **Returns:** `(subdomainsData: {
+ proxy: string;
+ underlyingAggregator: string;
+ proposedAggregator: string;
+})`
+ - `proxy`: Address of Data Feed Proxy
+ - `underlyingAggregator`: Address of Data Feed Offchain Aggregator
+ - `proposedAggregator`: Address of proposed Data Feed Offchain Aggregator
+- **Usage:**
+ ```typescript
+ const baseTick = 'ETH';
+ const quoteTick = 'USD';
+ const subdomainsData = await ens.resolveAggregatorAddressWithSubdomains(baseTick, quoteTick);
+ ```
## VRF Service
Chainlink [VRF](https://docs.chain.link/vrf/v2/introduction) (Verifiable Random Function) service is a critical component
provided by Chainlink that enables smart contracts on the blockchain to securely and transparently access cryptographically secure and unpredictable randomness.
-### Service alias: `vrf`
-
-This section provides methods and functionalities designed to interact with the VRFCoordinator smart contract,
+### Service alias: [`vrf`](src%2Fvrf%2FvrfCoordinator.ts)
+```typescript
+const vrf = hre.chainlink.vrf;
+```
+This section provides methods and functionalities designed to interact with the VRF Coordinator smart contract,
which serves as the intermediary between smart contracts on the blockchain and the VRF service.
-### Methods
-
#### Create Subscription
- **Method:** createSubscription
- **Description:** Create a new subscription to the VRF service
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(txData: {
+ subscriptionId: BigNumber;
+ transactionHash: string;
+})`
+ - `subscriptionId`: VRF Subscription ID
+ - `transactionHash`: Transaction hash of the transaction that created the subscription
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await vrf.createSubscription(vrfCoordinatorAddress);
+ ```
#### Fund Subscription
- **Method:** fundSubscription
- **Description:** Fund a subscription to the VRF service with LINK tokens
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, linkTokenAddress: string, amountInJuels: BigNumberish, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `linkTokenAddress`: Address of the LINK token contract
- `amountInJuels`: Amount of LINK tokens to be sent (in Juels)
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that funded the subscription
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const linkTokenAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const amountInJuels = 1000000000000000000;
+ const subscriptionId = 1;
+ const txData = await vrf.fundSubscription(vrfCoordinatorAddress, linkTokenAddress, amountInJuels, subscriptionId);
+ ```
#### Cancel Subscription
- **Method:** cancelSubscription
- **Description:** Cancel a subscription to the VRF service and receive the remaining balance
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, subscriptionId: BigNumberish, receivingAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `subscriptionId`: VRF Subscription ID
- `receivingAddress`: Address to receive the balance of the subscription
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that canceled the subscription
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const receivingAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await vrf.cancelSubscription(vrfCoordinatorAddress, subscriptionId, receivingAddress);
+ ```
#### Add Consumer
- **Method:** addConsumer
- **Description:** Add a new consumer to an existing VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, consumerAddress: string, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `consumerAddress`: Address of the consumer
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that added the consumer
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const consumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await vrf.addConsumer(vrfCoordinatorAddress, consumerAddress, subscriptionId);
+ ```
#### Remove Consumer
- **Method:** removeConsumer
- **Description:** Remove a consumer from an existing VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, consumerAddress: string, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `consumerAddress`: Address of the consumer
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that removed the consumer
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const consumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await vrf.removeConsumer(vrfCoordinatorAddress, consumerAddress, subscriptionId);
+ ```
#### Get Subscription Details
- **Method:** getSubscriptionDetails
- **Description:** Get details of an existing VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(vrfSubscriptionDetails: {
+ balance: BigNumber;
+ reqCount: BigNumber;
+ owner: string;
+ consumers: string[];
+})`
+ - `balance`: Balance of the subscription
+ - `reqCount`: Number of requests made to the subscription
+ - `owner`: Address of the subscription owner
+ - `consumers`: List of addresses of the consumers
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const vrfSubscriptionDetails = await vrf.getSubscriptionDetails(vrfCoordinatorAddress, subscriptionId);
+ ```
#### Request Random Words
- **Method:** requestRandomWords
- **Description:** Request random words from the VRF service
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, keyHash: BytesLike, subscriptionId: BigNumberish, requestConfirmations: BigNumberish, callbackGasLimit: BigNumberish, numWords: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `keyHash`: Key hash related to maxGasPrice of a VRF. Different keyHashes have different gas prices
- `subscriptionId`: VRF Subscription ID. Must be funded with the minimum subscription balance required for the selected keyHash
- `requestConfirmations`: How many blocks you'd like the oracle to wait before responding to the request
- `callbackGasLimit`: How much gas you allow for fulfillRandomWords callback
- `numWords`: The number of random values you'd like to receive in fulfillRandomWords callback
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that requested the random words
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const keyHash = '0x89630569c9567e43c4fe7b1633258df9f2531b62f2352fa721cf3162ee4ecb46';
+ const subscriptionId = 1;
+ const requestConfirmations = 10;
+ const callbackGasLimit = 2500000;
+ const numWords = 3;
+ const txData = await vrf.requestRandomWords(vrfCoordinatorAddress, keyHash, subscriptionId, requestConfirmations, callbackGasLimit, numWords);
+ ```
#### Check Pending Request
- **Method:** isPendingRequestExists
- **Description:** Check if there is a pending request for an existing VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(isPendingRequestExists: boolean)`
+ - `isPendingRequestExists`: Boolean value indicating if there is a pending request for the VRF subscription
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const isPendingRequestExists = await vrf.isPendingRequestExists(vrfCoordinatorAddress, subscriptionId);
+ ```
#### Request Subscription Owner Transfer
- **Method:** requestSubscriptionOwnerTransfer
- **Description:** Request to transfer ownership of a VRF subscription to a new owner
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, subscriptionId: BigNumberish, newOwnerAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `subscriptionId`: VRF Subscription ID
- `newOwnerAddress`: Address of the new subscription owner
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that requested the subscription owner transfer
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const newOwnerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await vrf.requestSubscriptionOwnerTransfer(vrfCoordinatorAddress, subscriptionId, newOwnerAddress);
+ ```
#### Accept Subscription Owner Transfer
- **Method:** acceptSubscriptionOwnerTransfer
- **Description:** Accept the transfer of ownership of a VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, subscriptionId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `subscriptionId`: VRF Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that accepted the subscription owner transfer
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await vrf.acceptSubscriptionOwnerTransfer(vrfCoordinatorAddress, subscriptionId);
+ ```
#### Get Maximum Consumers
- **Method:** getMaxConsumers
- **Description:** Get the maximum number of consumers that can be added to a VRF subscription
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(maxConsumers: number)`
+ - `maxConsumers`: Maximum number of consumers that can be added to a VRF subscription
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const maxConsumers = await vrf.getMaxConsumers(vrfCoordinatorAddress);
+ ```
#### Get Maximum Number of Words
- **Method:** getMaxNumberOfWords
- **Description:** Get the maximum number of random words that can be requested from the VRF service
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(maxNumberOfWords: number)`
+ - `maxNumberOfWords`: Maximum number of random words that can be requested from the VRF service
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const maxNumberOfWords = await vrf.getMaxNumberOfWords(vrfCoordinatorAddress);
+ ```
#### Get Maximum Request Confirmations
- **Method:** getMaxRequestConfirmations
-- **Description:** Get the maximum number of block confirmations allowed for a VRF request
-- **Arguments:**
+- **Description:** Get the maximum number of confirmations allowed for a VRF request
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(maxRequestConfirmations: number)`
+ - `maxRequestConfirmations`: Maximum number of confirmations allowed for a VRF request
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const maxRequestConfirmations = await vrf.getMaxRequestConfirmations(vrfCoordinatorAddress);
+ ```
#### Get Minimum Request Confirmations
- **Method:** getMinRequestConfirmations
-- **Description:** Get the minimum number of block confirmations required for a VRF request
-- **Arguments:**
+- **Description:** Get the minimum number of confirmations required for a VRF request
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(minRequestConfirmations: number)`
+ - `minRequestConfirmations`: Minimum number of confirmations required for a VRF request
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const minRequestConfirmations = await vrf.getMinRequestConfirmations(vrfCoordinatorAddress);
+ ```
#### Get Maximum Request Gas Limit
- **Method:** getMaxRequestGasLimit
- **Description:** Get the maximum gas limit for a VRF request
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(maxRequestGasLimit: number)`
+ - `maxRequestGasLimit`: Maximum gas limit for a VRF request
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const maxRequestGasLimit = await vrf.getMaxRequestGasLimit(vrfCoordinatorAddress);
+ ```
#### Get Commitment
- **Method:** getCommitment
- **Description:** Get the commitment data for a VRF request
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string, requestId: BigNumberish)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
- `requestId`: Request ID
+- **Returns:** `(commitmentData: BytesLike)`
+ - `commitmentData`: Commitment data for a VRF request
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const requestId = 1;
+ const commitmentData = await vrf.getCommitment(vrfCoordinatorAddress, requestId);
+ ```
#### Get Configuration
- **Method:** getConfig
- **Description:** Get the configuration details of VRF Coordinator
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
+- **Returns:** `(configData: {
+ minimumRequestConfirmations: number;
+ maxGasLimit: number;
+ stalenessSeconds: number;
+ gasAfterPaymentCalculation: number;
+})`
+- `minimumRequestConfirmations`: Minimum number of confirmations required for a VRF request
+- `maxGasLimit`: Maximum gas limit for a VRF request
+- `stalenessSeconds`: Number of seconds after which a VRF request is considered stale
+- `gasAfterPaymentCalculation`: Gas used in doing accounting for a VRF request after gas measurement
#### Get Type and Version
- **Method:** getTypeAndVersion
- **Description:** Get the type and version details of VRF Coordinator
-- **Arguments:**
+- **Arguments:** `(vrfCoordinatorAddress: string)`
- `vrfCoordinatorAddress`: Address of VRF Coordinator contract
-
+- **Returns:** `(typeAndVersion: string)`
+ - `typeAndVersion`: Type and version details of VRF Coordinator
+- **Usage:**
+ ```typescript
+ const vrfCoordinatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const typeAndVersion = await vrf.getTypeAndVersion(vrfCoordinatorAddress);
+ ```
## Automation Services
-Chainlink [Automation](https://docs.chain.link/vrf/v2/introduction) service that enables conditional execution
+Chainlink [Automation](https://docs.chain.link/vrf/v2/introduction) service enables conditional execution
of your smart contracts functions through a hyper-reliable and decentralized automation platform.
-### Service alias: `automationRegistrar`
-
-This section provides methods and functionalities designed to interact with the KeeperRegistrar smart contract,
-which accepts requests for upkeep registrations.
-
-### Methods
+### Service alias: [`automationRegistrar`](src%2Fautomation%2FkeepersRegistrar.ts)
+```typescript
+const automationRegistrar = hre.chainlink.automationRegistrar;
+```
+This section provides methods and functionalities designed to interact with the KeeperRegistrar smart contract.
+Supported versions of Keeper Registrar are v1.2, v2.0.
#### Register Upkeep
- **Method:** registerUpkeep
- **Description:** Register an upkeep task for Chainlink Keepers to perform on a specified contract
-- **Arguments:**
+- **Arguments:** `(keeperRegistrarAddress: string, linkTokenAddress: string, amountInJuels: BigNumberish, upkeepName: string, encryptedEmail: string, upkeepContract: string, gasLimit: BigNumberish, adminAddress: string, checkData: BytesLike, ocrConfig: BytesLike, source: BigNumberish, sender: string)`
- `keeperRegistrarAddress`: Address of Keeper Registrar
- `linkTokenAddress`: Address of Link Token
- `amountInJuels`: Amount of LINK in juels to fund the upkeep
@@ -707,330 +1306,1025 @@ which accepts requests for upkeep registrations.
- `ocrConfig`: OffchainConfig for upkeep in bytes [Keeper Registrar v2_0 ONLY], default value: "0x"
- `source`: ID of the application sending this request [Keeper Registrar v1_1 ONLY], default value: "0"
- `sender`: Address of the sender making the request
+- **Returns:** `(txData: {
+ transactionHash: string;
+ requestHash: string;
+ upkeepId: BigNumber;
+})`
+ - `transactionHash`: Transaction hash of the transaction that registered the upkeep
+ - `requestHash`: Hash of the registration request
+ - `upkeepId`: Upkeep ID
+- **Usage:**
+ ```typescript
+ const keeperRegistrarAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const linkTokenAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const amountInJuels = 1000000000000000000;
+ const upkeepName = 'upkeepName';
+ const encryptedEmail = '0x';
+ const upkeepContract = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const gasLimit = 2500000;
+ const adminAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const checkData = '0x';
+ const ocrConfig = '0x';
+ const source = 1;
+ const sender = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await automationRegistrar.registerUpkeep(keeperRegistrarAddress, linkTokenAddress, amountInJuels, upkeepName, encryptedEmail, upkeepContract, gasLimit, adminAddress, checkData, ocrConfig, source, sender);
+ ```
#### Get Pending Request
- **Method:** getPendingRequest
- **Description:** Get information about a pending registration request for an upkeep task
-- **Arguments:**
+- **Arguments:** `(keeperRegistrarAddress: string, requestHash: BytesLike)`
- `keeperRegistrarAddress`: Address of Keeper Registrar
- `requestHash`: Hash of the registration request
+- **Returns:** `(pendingRequestData: {
+ adminAddress: string;
+ balance: BigNumber;
+})`
+ - `adminAddress`: Address to cancel upkeep and withdraw remaining funds
+ - `balance`: Balance of the upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistrarAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const requestHash = '0x';
+ const pendingRequestData = await automationRegistrar.getPendingRequest(keeperRegistrarAddress, requestHash);
+ ```
#### Cancel Request
- **Method:** cancelRequest
- **Description:** Cancel a pending registration request for an upkeep task
-- **Arguments:**
+- **Arguments:** `(keeperRegistrarAddress: string, requestHash: BytesLike)`
- `keeperRegistrarAddress`: Address of Keeper Registrar
- `requestHash`: Hash of the registration request
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that canceled the request
+- **Usage:**
+ ```typescript
+ const keeperRegistrarAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const requestHash = '0x';
+ const txData = await automationRegistrar.cancelRequest(keeperRegistrarAddress, requestHash);
+ ```
#### Get Registration Config
- **Method:** getRegistrationConfig
- **Description:** Get the registration configuration for upkeep tasks from the Keeper Registrar
-- **Arguments:**
+- **Arguments:** `(keeperRegistrarAddress: string)`
- `keeperRegistrarAddress`: Address of Keeper Registrar
+- **Returns:** `(registrationConfig: {
+ autoApproveConfigType: number;
+ autoApproveMaxAllowed: number;
+ approvedCount: number;
+ keeperRegistry: string;
+ minLINKJuels: BigNumber;
+})`
+ - `autoApproveConfigType`: Auto approve configuration type
+ - `autoApproveMaxAllowed`: Maximum number of upkeep tasks that can be auto approved
+ - `approvedCount`: Number of upkeep tasks that have been auto approved
+ - `keeperRegistry`: Address of Keeper Registry
+ - `minLINKJuels`: Minimum amount of LINK in juels required to register an upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistrarAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const registrationConfig = await automationRegistrar.getRegistrationConfig(keeperRegistrarAddress);
+ ```
#### Get Type And Version
- **Method:** getTypeAndVersion
- **Description:** Get the type and version for the Keeper Registrar
-- **Arguments:**
+- **Arguments:** `(keeperRegistrarAddress: string)`
- `keeperRegistrarAddress`: Address of Keeper Registrar
-
-### Service alias: `automationRegistry`
-
-This section provides methods and functionalities designed to interact with the KeeperRegistry smart contract,
-which serves to add tasks for Chainlink Keepers to perform on client contracts.
-
-### Methods
+- **Returns:** `(typeAndVersion: string)`
+ - `typeAndVersion`: Type and version for the Keeper Registrar
+- **Usage:**
+ ```typescript
+ const keeperRegistrarAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const typeAndVersion = await automationRegistrar.getTypeAndVersion(keeperRegistrarAddress);
+ ```
+
+### Service alias: [`automationRegistry`](src%2Fautomation%2FkeepersRegistry.ts)
+```typescript
+const automationRegistry = hre.chainlink.automationRegistry;
+```
+This section provides methods and functionalities designed to interact with the KeeperRegistry smart contract.
+Supported versions of Keeper Registry are v1.2, v1.3, v2.0.
#### Get State
- **Method:** getState
- **Description:** Get the current state of Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
+- **Returns:** `(stateData: {
+ nonce: number;
+ ownerLinkBalance: BigNumber;
+ expectedLinkBalance: BigNumber;
+ numUpkeeps: BigNumber;
+ paymentPremiumPPB: number;
+ flatFeeMicroLink: number;
+ blockCountPerTurn: number | undefined;
+ checkGasLimit: number;
+ stalenessSeconds: number;
+ gasCeilingMultiplier: number;
+ minUpkeepSpend: BigNumber;
+ maxPerformGas: number;
+ fallbackGasPrice: BigNumber;
+ fallbackLinkPrice: BigNumber;
+ transcoder: string;
+ registrar: string;
+ keepers: string[] | undefined;
+})`
+ - `nonce`: Nonce of the Keeper Registry
+ - `ownerLinkBalance`: LINK balance of the owner
+ - `expectedLinkBalance`: Expected LINK balance of the owner
+ - `numUpkeeps`: Number of upkeep tasks registered
+ - `paymentPremiumPPB`: Payment premium rate oracles receive on top of being reimbursed for gas, measured in parts per billion
+ - `flatFeeMicroLink`: Flat fee paid to oracles for performing upkeeps in micro LINK
+ - `blockCountPerTurn`: Number of blocks per turn [Keeper Registry v1_2, v1_3 ONLY]
+ - `checkGasLimit`: Gas limit when checking for upkeep
+ - `stalenessSeconds`: Number of seconds after which a Keeper is considered stale
+ - `gasCeilingMultiplier`: Multiplier for gas ceiling
+ - `minUpkeepSpend`: Minimum amount of LINK in juels an upkeep must spend before cancelling
+ - `maxPerformGas`: Maximum amount of gas allowed for an upkeep to perform on this registry
+ - `fallbackGasPrice`: Fallback gas price
+ - `fallbackLinkPrice`: Fallback LINK price
+ - `transcoder`: Address of the transcoder
+ - `registrar`: Address of the registrar
+ - `keepers`: List of addresses of the keepers [Keeper Registry v2_0 ONLY]
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const stateData = await automationRegistry.getState(keeperRegistryAddress);
+ ```
#### Get Active Upkeep IDs
- **Method:** getActiveUpkeepIDs
- **Description:** Get a list of active upkeep IDs from Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, startIndex: BigNumberish, maxCount: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `startIndex`: Starting index of Upkeeps to get
- `maxCount`: Quantity of Upkeeps to get
+- **Returns:** `(activeUpkeepIds: BigNumber[])`
+ - `activeUpkeepIds`: List of active upkeep IDs
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const startIndex = 0;
+ const maxCount = 10;
+ const activeUpkeepIds = await automationRegistry.getActiveUpkeepIDs(keeperRegistryAddress, startIndex, maxCount);
+ ```
#### Get Max Payment For Gas
- **Method:** getMaxPaymentForGas
- **Description:** Get maximum payment for the gas limit, calculated using the Keeper Registry parameters
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, gasLimit: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `gasLimit`: Gas limit
+- **Returns:** `(maxPaymentForGas: BigNumber)`
+ - `maxPaymentForGas`: Maximum payment for a given gas limit
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const gasLimit = 2500000;
+ const maxPaymentForGas = await automationRegistry.getMaxPaymentForGas(keeperRegistryAddress, gasLimit);
+ ```
#### Is Paused
- **Method:** isPaused
- **Description:** Check if the Keeper Registry is currently paused
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
+- **Returns:** `(isPaused: boolean)`
+ - `isPaused`: Boolean value indicating if the Keeper Registry is currently paused
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const isPaused = await automationRegistry.isPaused(keeperRegistryAddress);
+ ```
#### Get Upkeep
- **Method:** getUpkeep
- **Description:** Get information about a specific upkeep from the Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepId: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepId`: Upkeep ID
+- **Returns:** `(upkeepData: {
+ target: string;
+ executeGas: number;
+ checkData: BytesLike;
+ balance: BigNumber;
+ lastKeeper: string | undefined;
+ admin: string;
+ maxValidBlocknumber: BigNumber;
+ amountSpent: BigNumber;
+})`
+ - `target`: Address of the contract to perform upkeep on
+ - `executeGas`: Gas limit to provide the target contract when performing upkeep
+ - `checkData`: Data passed to the contract when checking upkeep
+ - `balance`: Balance of the upkeep
+ - `lastKeeper`: Keeper which last performs the upkeep [Keeper Registry v1_2, v1_3 ONLY]
+ - `admin`: Address of upkeep admin
+ - `maxValidBlocknumber`: Maximum block number for the upkeep to be performed
+ - `amountSpent`: Amount of LINK in juels spent on the upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepId = 1;
+ const upkeepData = await automationRegistry.getUpkeep(keeperRegistryAddress, upkeepId);
+ ```
#### Get Min Balance For Upkeep
- **Method:** getMinBalanceForUpkeep
- **Description:** Get the minimum required balance for upkeep from the Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepId: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepId`: Upkeep ID
+- **Returns:** `(minBalanceForUpkeep: BigNumber)`
+ - `minBalanceForUpkeep`: Minimum required balance for upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepId = 1;
+ const minBalanceForUpkeep = await automationRegistry.getMinBalanceForUpkeep(keeperRegistryAddress, upkeepId);
+ ```
#### Fund Upkeep
- **Method:** fundUpkeep
- **Description:** Fund an upkeep task with a specified amount of LINK in Juels
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepId: BigNumberish, amountInJuels: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepId`: Upkeep ID
- `amountInJuels`: Amount of LINK in Juels to fund upkeep
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that funded the upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepId = 1;
+ const amountInJuels = 1000000000000000000;
+ const txData = await automationRegistry.fundUpkeep(keeperRegistryAddress, upkeepId, amountInJuels);
+ ```
#### Cancel Upkeep
- **Method:** cancelUpkeep
- **Description:** Cancel an upkeep task in Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepId: BigNumberish)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepId`: Upkeep ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that canceled the upkeep
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepId = 1;
+ const txData = await automationRegistry.cancelUpkeep(keeperRegistryAddress, upkeepId);
+ ```
#### Withdraw Funds
- **Method:** withdrawFunds
- **Description:** Withdraw funds from an upkeep task in Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepId: BigNumberish, receivingAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepId`: Upkeep ID
- `receivingAddress`: Address to withdraw funds
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that withdrew the funds
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepId = 1;
+ const receivingAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await automationRegistry.withdrawFunds(keeperRegistryAddress, upkeepId, receivingAddress);
+ ```
#### Migrate Upkeeps
- **Method:** migrateUpkeeps
- **Description:** Migrate multiple upkeep tasks to another Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, upkeepIds: BigNumberish[], destination: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `upkeepIds`: Upkeep IDs to migrate
- `destination`: Address of destination Keeper Registry
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that migrated the upkeeps
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepIds = [1, 2, 3];
+ const destination = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await automationRegistry.migrateUpkeeps(keeperRegistryAddress, upkeepIds, destination);
+ ```
#### Get Keeper Info
- **Method:** getKeeperInfo
- **Description:** Get information about a specific keeper from Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string, keeperAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
- `keeperAddress`: Keeper address
+- **Returns:** `(keeperInfo: {
+ payee: string;
+ active: boolean;
+ balance: BigNumber;
+})`
+ - `payee`: Address of the payee
+ - `active`: Boolean value indicating if the keeper is active
+ - `balance`: Balance of the keeper
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const keeperAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const keeperInfo = await automationRegistry.getKeeperInfo(keeperRegistryAddress, keeperAddress);
+ ```
#### Get Type And Version
- **Method:** getTypeAndVersion
- **Description:** Get the type and version for Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
+- **Returns:** `(typeAndVersion: string)`
+ - `typeAndVersion`: Type and version for Keeper Registry
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const typeAndVersion = await automationRegistry.getTypeAndVersion(keeperRegistryAddress);
+ ```
#### Get Upkeep Transcoder Version
- **Method:** getUpkeepTranscoderVersion
- **Description:** Get the upkeep transcoder version from Keeper Registry
-- **Arguments:**
+- **Arguments:** `(keeperRegistryAddress: string)`
- `keeperRegistryAddress`: Address of Keeper Registry
+- **Returns:** `(upkeepTranscoderVersion: string)`
+ - `upkeepTranscoderVersion`: Upkeep transcoder version from Keeper Registry
+- **Usage:**
+ ```typescript
+ const keeperRegistryAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const upkeepTranscoderVersion = await automationRegistry.getUpkeepTranscoderVersion(keeperRegistryAddress);
+ ```
+
+## Functions Service
+
+Chainlink [Functions](https://docs.chain.link/chainlink-functions) service provides your smart contracts access to
+trust-minimized compute infrastructure, allowing you to fetch data from APIs and perform custom computation.
+
+> **Note**
+> Most of the methods under this section are thin wrappers for the methods provided by
+> the [functions-toolkit](https://github.com/smartcontractkit/functions-toolkit) NPM package.
+
+### Service alias: [`functions`](src%2Ffunctions%2Findex.ts)
+```typescript
+const functions = hre.chainlink.functions;
+```
+This section provides methods and functionalities designed to interact with the Functions service.
+
+#### Create subscription
+
+- **Method:** createSubscription
+- **Description:** Create Functions subscription
+- **Arguments:** `(functionsRouterAddress: string, consumerAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `consumerAddress`: Address of Functions Consumer (Default: "")
+- **Returns:** `(subscriptionId: BigNumber)`
+ - `subscriptionId`: Subscription ID
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const consumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = await functions.createSubscription(functionsRouterAddress, consumerAddress);
+ ```
+
+#### Fund subscription
+
+- **Method:** fundSubscription
+- **Description:** Fund Functions subscription
+- **Arguments:** `(functionsRouterAddress: string, linkTokenAddress: string, amountInJuels: BigNumberish, subscriptionId: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `linkTokenAddress`: Address of Link Token
+ - `amountInJuels`: Amount of LINK in Juels to fund Subscription
+ - `subscriptionId`: Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that funded the subscription
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const linkTokenAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const amountInJuels = 1000000000000000000;
+ const subscriptionId = 1;
+ const txData = await functions.fundSubscription(functionsRouterAddress, linkTokenAddress, amountInJuels, subscriptionId);
+ ```
+
+#### Cancel subscription
+
+- **Method:** cancelSubscription
+- **Description:** Cancel Functions subscription
+- **Arguments:** `(functionsRouterAddress: string, subscriptionId: BigNumberish, receivingAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `subscriptionId`: Subscription ID
+ - `receivingAddress`: Address to receive the balance of Subscription (Default: "")
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that canceled the subscription
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const receivingAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await functions.cancelSubscription(functionsRouterAddress, subscriptionId, receivingAddress);
+ ```
+
+#### Get subscription details
+
+- **Method:** getSubscriptionDetails
+- **Description:** Get subscription details
+- **Arguments:** `(functionsRouterAddress: string, subscriptionId: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `subscriptionId`: Subscription ID
+- **Returns:** `(subscriptionDetails: {
+ balance: BigNumber;
+ owner: string;
+ blockedBalance: BigNumber;
+ proposedOwner: string;
+ consumers: string[];
+ flags: string;
+})`
+ - `balance`: Balance of the subscription
+ - `owner`: Address of the subscription owner
+ - `blockedBalance`: Blocked balance of the subscription
+ - `proposedOwner`: Address of the proposed subscription owner
+ - `consumers`: List of addresses of the consumers
+ - `flags`: Subscription flags
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const subscriptionDetails = await functions.getSubscriptionDetails(functionsRouterAddress, subscriptionId);
+ ```
+
+#### Request subscription owner transfer
+
+- **Method:** requestSubscriptionOwnerTransfer
+- **Description:** Request subscription owner transfer
+- **Arguments:** `(functionsRouterAddress: string, subscriptionId: BigNumberish, newOwnerAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `subscriptionId`: Subscription ID
+ - `newOwnerAddress`: Address of new owner of Subscription
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that requested the subscription owner transfer
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const newOwnerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await functions.requestSubscriptionOwnerTransfer(functionsRouterAddress, subscriptionId, newOwnerAddress);
+ ```
+
+#### Accept subscription owner transfer
+
+- **Method:** acceptSubscriptionOwnerTransfer
+- **Description:** Accept subscription owner transfer
+- **Arguments:** `(functionsRouterAddress: string, subscriptionId: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `subscriptionId`: Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that accepted the subscription owner transfer
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await functions.acceptSubscriptionOwnerTransfer(functionsRouterAddress, subscriptionId);
+ ```
+
+#### Add subscription consumer
+
+- **Method:** addConsumer
+- **Description:** Add subscription consumer
+- **Arguments:** `(functionsRouterAddress: string, consumerAddress: string, subscriptionId: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `consumerAddress`: Address of Functions Consumer
+ - `subscriptionId`: Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that added the consumer
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const consumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await functions.addConsumer(functionsRouterAddress, consumerAddress, subscriptionId);
+ ```
+
+#### Remove subscription consumer
+
+- **Method:** removeConsumer
+- **Description:** Remove subscription consumer
+- **Arguments:** `(functionsRouterAddress: string, consumerAddress: string, subscriptionId: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `consumerAddress`: Address of Functions Consumer
+ - `subscriptionId`: Subscription ID
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that removed the consumer
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const consumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const txData = await functions.removeConsumer(functionsRouterAddress, consumerAddress, subscriptionId);
+ ```
+
+#### Timeout requests
+
+##### Task/Subtask/CLI
+- **Method:** timeoutRequests
+- **Description:** Timeout pending Functions request
+- **Arguments:** `(functionsRouterAddress: string, requestIdsString: string, donId: string, toBlock: string, pastBlocksToSearch: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `requestIdsString`: Comma-separated requests IDs
+ - `donId`: ID of the DON where Functions requests has been sent
+ - `toBlock`: End block in search range (Default: "latest")
+ - `pastBlocksToSearch`: Number of blocks to search (before toBlock) (Default: "1000")
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that timed out the requests
+
+##### HRE
+- **Method:** timeoutRequests
+- **Description:** Timeout pending Functions request
+- **Arguments:** `(functionsRouterAddress: string, requestCommitments: RequestCommitment[])`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `requestCommitments`: Array of RequestCommitment objects
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that timed out the requests
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const requestCommitments = [];
+ const txData = await functions.timeoutRequests(functionsRouterAddress, requestCommitments);
+ ```
+
+#### Estimate the cost of a request
+- **Method:** estimateRequestCost
+- **Description:** Estimate the cost of a request
+- **Arguments:** `(functionsRouterAddress: string, donId: string, subscriptionId: BigNumberish, callbackGasLimit: number, gasPriceWei: BigNumberish)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to which the Functions request will be sent
+ - `subscriptionId`: Subscription ID
+ - `callbackGasLimit`: Total gas used by the consumer contract's callback
+ - `gasPriceWei`: Gas price in wei
+- **Returns:** `(requestCost: BigInt)`
+ - `requestCost`: Cost of the request
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const subscriptionId = 1;
+ const callbackGasLimit = 2500000;
+ const gasPriceWei = 1000000000;
+ const requestCost = await functions.estimateRequestCost(functionsRouterAddress, donId, subscriptionId, callbackGasLimit, gasPriceWei);
+ ```
+
+> **Note**
+> The following are methods that are available only in HRE
+
+#### Initialize SubscriptionManager class
+- **Method:** initializeSubscriptionManager
+- **Description:** Initialize [SubscriptionManager class](https://github.com/smartcontractkit/functions-toolkit#functions-billing-subscription-management)
+- **Arguments:** `(functionsRouterAddress: string, linkTokenAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `linkTokenAddress`: Address of Link Token
+- **Returns:** `(subscriptionManager: FunctionsSubscriptionManager)`
+ - `subscriptionManager`: SubscriptionManager class instance
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const linkTokenAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionManager = await functions.initializeSubscriptionManager(functionsRouterAddress, linkTokenAddress);
+ ```
+
+#### Initialize ResponseListener class
+- **Method:** initializeResponseListener
+- **Description:** Initialize [ResponseListener class](https://github.com/smartcontractkit/functions-toolkit#functions-response-listener)
+- **Arguments:** `(functionsRouterAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+- **Returns:** `(responseListener: FunctionsResponseListener)`
+ - `responseListener`: ResponseListener class instance
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const responseListener = await functions.initializeResponseListener(functionsRouterAddress);
+ ```
+
+#### Initialize SecretsManager class
+- **Method:** initializeSecretsManager
+- **Description:** Initialize [SecretsManager class](https://github.com/smartcontractkit/functions-toolkit#functions-encrypted-secrets-management)
+- **Arguments:** `(functionsRouterAddress: string, donId: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+- **Returns:** `(secretsManager: FunctionsSecretsManager)`
+ - `secretsManager`: SecretsManager class instance
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const secretsManager = await functions.initializeSecretsManager(functionsRouterAddress, donId);
+ ```
+
+#### Listen for a response for single Functions request
+- **Method:** listenForResponse
+- **Description:** Listen for a response for single Functions request
+- **Arguments:** `(functionsRouterAddress: string, requestId: BigNumberish, timeout: number)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `requestId`: Request ID
+ - `timeout`: Subscription ID (Default: 300_000)
+- **Returns:** `(response: FunctionsResponse)`
+ - `response`: Response of the request
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const requestId = 1;
+ const timeout = 300000;
+ const response = await functions.listenForResponse(functionsRouterAddress, requestId, timeout);
+ ```
+
+#### Listen for a response for single Functions request (from transaction)
+- **Method:** listenForResponseFromTransaction
+- **Description:** Listen for a response for single Functions request (from transaction)
+- **Arguments:** `(functionsRouterAddress: string, transactionHash: string, timeout: number, confirmations: number, checkInterval: number)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `transactionHash`: Transaction hash to start listen from
+ - `timeout`: Listener timeout (Default: 300000)
+ - `confirmations`: Number of block confirmations (Default: 2)
+ - `checkInterval`: Frequency of checking for a response to appear on-chain (Default: 2000)
+- **Returns:** `(response: FunctionsResponse)`
+ - `response`: Response of the request
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const transactionHash = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const timeout = 300000;
+ const confirmations = 2;
+ const checkInterval = 2000;
+ const response = await functions.listenForResponseFromTransaction(functionsRouterAddress, transactionHash, timeout, confirmations, checkInterval);
+ ```
+
+#### Listen for responses
+- **Method:** listenForResponses
+- **Description:** Listen for multiple Functions responses
+- **Arguments:** `(functionsRouterAddress: string, subscriptionId: BigNumberish, callback: (response: FunctionsResponse) => any)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `subscriptionId`: Subscription ID
+ - `callback`: Callback to be called when responses are fulfilled
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subscriptionId = 1;
+ const callback = (response: FunctionsResponse) => {
+ console.log(response);
+ };
+ await functions.listenForResponses(functionsRouterAddress, subscriptionId, callback);
+ ```
+
+#### Stop response listener
+- **Method:** stopListeningForResponses
+- **Description:** Stop response listener
+- **Arguments:** `(functionsRouterAddress: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ await functions.stopListeningForResponses(functionsRouterAddress);
+ ```
+
+#### Fetch DON public keys
+- **Method:** fetchKeys
+- **Description:** Fetch DON public keys
+- **Arguments:** `(functionsRouterAddress: string, donId: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to fetch keys from
+- **Returns:** `(keys: {
+ thresholdPublicKey: ThresholdPublicKey;
+ donPublicKey: string;
+})`
+ - `thresholdPublicKey`: Threshold public key
+ - `donPublicKey`: DON public key
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const keys = await functions.fetchKeys(functionsRouterAddress, donId);
+ ```
+
+#### Encrypt Off-Chain secrets URLs
+- **Method:** encryptSecretsUrls
+- **Description:** Encrypt Off-Chain Secrets URLs with DON public key to produce encryptedSecretsReference
+- **Arguments:** `(functionsRouterAddress: string, donId: string, secretsUrls: string[])`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to fetch keys from
+ - `secretsUrls`: URLs of secrets
+- **Returns:** `(encryptedSecretsReference: string)`
+ - `encryptedSecretsReference`: HEX string of encrypted secrets
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const secretsUrls = ['https://gist.github.com/username/secret'];
+ const encryptedSecretsReference = await functions.encryptSecretsUrls(functionsRouterAddress, donId, secretsUrls);
+ ```
+
+#### Verify Off-Chain secrets URLs
+- **Method:** verifyOffchainSecrets
+- **Description:** Verify if provided Gists secret URLs are valid
+- **Arguments:** `(functionsRouterAddress: string, donId: string, secretsUrls: string[])`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to fetch keys from
+ - `secretsUrls`: URLs of secrets
+- **Returns:** `(isValid: boolean)`
+ - `isValid`: True if all secrets URLs are valid
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const secretsUrls = ['https://gist.github.com/username/secret'];
+ const isValid = await functions.verifyOffchainSecrets(functionsRouterAddress, donId, secretsUrls);
+ ```
+
+#### Encrypt secrets
+- **Method:** encryptSecrets
+- **Description:** Encrypt secrets with DON public key to produce encryptedSecretsHexstring
+- **Arguments:** `(functionsRouterAddress: string, donId: string, secrets: string)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+ - `secrets`: String key/value pairs of secrets
+- **Returns:** `(encryptedSecretsData: {
+ encryptedSecrets: string;
+})`
+ - `encryptedSecrets`: HEX string of encrypted secrets
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const secrets = {'test': 'test'};
+ const encryptedSecretsData = await functions.encryptSecrets(functionsRouterAddress, donId, secrets);
+ ```
+
+#### Upload encrypted secrets to DON
+- **Method:** uploadEncryptedSecretsToDON
+- **Description:** Upload encrypted secrets to DON
+- **Arguments:** `(functionsRouterAddress: string, donId: string, encryptedSecretsHexstring: string, gatewayUrls: string[], slotId: number, minutesUntilExpiration: number)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+ - `encryptedSecretsHexstring`: HEX string of encrypted secrets. Result of encryptSecrets() method
+ - `gatewayUrls`: DON gateway URLs
+ - `slotId`: Storage slot ID, can be any integer value of zero or greater
+ - `minutesUntilExpiration`: Minutes after secrets will be deleted from all DON nodes
+- **Returns:** `(uploadDetails: {
+ version: number;
+ success: boolean;
+})`
+ - `version`: Version of the uploaded secrets
+ - `success`: True if secrets were uploaded successfully
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const encryptedSecretsHexstring = '0x89630569c9567e43c4fe7b1633258df9f2531b62f2352fa721cf3162ee4ecb46';
+ const gatewayUrls = ['https://gateway.don.dev'];
+ const slotId = 1;
+ const minutesUntilExpiration = 60;
+ const uploadDetails = await functions.uploadEncryptedSecretsToDON(functionsRouterAddress, donId, encryptedSecretsHexstring, gatewayUrls, slotId, minutesUntilExpiration);
+ ```
+
+#### List DON hosted encrypted secrets
+- **Method:** listDONHostedEncryptedSecrets
+- **Description:** Get list of DON hosted encrypted secrets
+- **Arguments:** `(functionsRouterAddress: string, donId: string, gatewayUrls: string[])`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+ - `gatewayUrls`: DON gateway URLs
+- **Returns:** `(secretsList: {
+ result: GatewayResponse;
+ error?: string;
+})`
+ - `result`: Gateway response
+ - `error`: Error message
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const gatewayUrls = ['https://gateway.don.dev'];
+ const secretsList = await functions.listDONHostedEncryptedSecrets(functionsRouterAddress, donId, gatewayUrls);
+ ```
+
+#### Build DON hosted encrypted secrets reference
+- **Method:** buildDONHostedEncryptedSecretsReference
+- **Description:** Build DON hosted encrypted secrets reference (encryptedSecretsReference)
+- **Arguments:** `(functionsRouterAddress: string, donId: string, slotId: number, version: number)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+ - `slotId`: Storage slot ID, can be any integer value of zero or greater
+ - `version`: Reference version, any integer value of zero or greater
+- **Returns:** `(encryptedSecretsReference: string)`
+ - `encryptedSecretsReference`: HEX string of encrypted secrets reference
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const slotId = 1;
+ const version = 1;
+ const encryptedSecretsReference = await functions.buildDONHostedEncryptedSecretsReference(functionsRouterAddress, donId, slotId, version);
+ ```
+
+#### Fetch Request Commitments
+- **Method:** fetchRequestCommitment
+- **Description:** Fetch Commitments for Function request
+- **Arguments:** `(functionsRouterAddress: string, donId: string, requestId: string, toBlock: number, pastBlocksToSearch: number)`
+ - `functionsRouterAddress`: Address of Functions Router
+ - `donId`: ID of the DON to work with
+ - `requestId`: Request ID
+ - `toBlock`: Ending block number to search for the request commitment
+ - `pastBlocksToSearch`: Number of blocks from the ending block to search for the request commitment
+- **Returns:** `(requestCommitment: RequestCommitment)`
+ - `requestCommitment`: RequestCommitment object
+- **Usage:**
+ ```typescript
+ const functionsRouterAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const donId = '1';
+ const requestId = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const toBlock = 1000000;
+ const pastBlocksToSearch = 1000;
+ const requestCommitment = await functions.fetchRequestCommitment(functionsRouterAddress, donId, requestId, toBlock, pastBlocksToSearch);
+ ```
## Utilities
The plugin utility methods.
-### Service alias: `utils`
-
-### Methods
+### Service alias: [`utils`](src%2Futils%2Findex.ts)
+```typescript
+const utils = hre.chainlink.utils;
+```
#### Get Data Feed Proxy/Registry Round ID
- **Method:** getRoundId
- **Description:** Obtain the Data Feed Proxy/Registry round ID using the provided phase ID and aggregator round ID
-- **Arguments:**
+- **Arguments:** `(phaseId: BigNumberish, aggregatorRoundId: BigNumberish)`
- `phaseId`: Data Feed Proxy/Registry phase ID
- `aggregatorRoundId`: Data Feed round ID
+- **Returns:** `(roundId: BigNumber)`
+ - `roundId`: Data Feed Proxy/Registry round ID
+- **Usage:**
+ ```typescript
+ const phaseId = 1;
+ const aggregatorRoundId = 1;
+ const roundId = await utils.getRoundId(phaseId, aggregatorRoundId);
+ ```
#### Parse Data Feed Proxy/Registry Round ID
- **Method:** parseRoundId
- **Description:** Parse the given Data Feed Proxy/Registry round ID to extract relevant information
-- **Arguments:**
+- **Arguments:** `(roundId: BigNumberish)`
- `roundId`: Data Feed Proxy/Registry round ID
+- **Returns:** `(roundIdData: {
+ phaseId: BigNumber;
+ aggregatorRoundId: BigNumber;
+})`
+ - `phaseId`: Data Feed Proxy/Registry phase ID
+ - `aggregatorRoundId`: Data Feed round ID
+- **Usage:**
+ ```typescript
+ const roundId = 1;
+ const roundIdData = await utils.parseRoundId(roundId);
+ ```
#### Transfer ETH
- **Method:** transferETH
- **Description:** Transfer ETH to recipient
-- **Arguments:**
+- **Arguments:** `(recipient: string, amount: BigNumberish)`
- `recipient`: Recipient address
- `amount`: Amount of ETH in wei
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that transferred ETH
+- **Usage:**
+ ```typescript
+ const recipient = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const amount = 1000000000000000000;
+ const txData = await utils.transferETH(recipient, amount);
+ ```
+
+#### Create GitHub gist
+
+- **Method:** createGist
+- **Description:** Create private GitHub gist
+- **Arguments:** `(githubApiToken: string, content: string)`
+ - `githubApiToken`: GitHub API token
+ - `content`: Gist content
+- **Returns:** `(gistId: string)`
+ - `gistId`: Gist ID
+- **Usage:**
+ ```typescript
+ const githubApiToken = '8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const content = 'test';
+ const gistId = await utils.createGist(githubApiToken, content);
+ ```
+
+#### Delete GitHub gist
+
+- **Method:** deleteGist
+- **Description:** Delete private GitHub gist
+- **Arguments:** `(githubApiToken: string, gistURL: string)`
+ - `githubApiToken`: GitHub API token
+ - `gistURL`: Gist URL
+- **Returns:** `(success: boolean)`
+ - `success`: True if gist was deleted successfully
+- **Usage:**
+ ```typescript
+ const githubApiToken = '8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const gistURL = 'https://gist.github.com/username/secret';
+ const success = await utils.deleteGist(githubApiToken, gistURL);
+ ```
-## Sandbox `sandbox`
-
-This module contains a group of services for starting and managing a Chainlink node and setting up Chainlink jobs.
-
-### Service alias: `node`
-
-This section provides methods and functionalities designed to spin up and manage Chainlink node.
-
-### Methods
-
-#### Run Chainlink node
-
-- **Method:** run
-- **Description:** Run local Chainlink node
-
-#### Restart Chainlink node
-
-- **Method:** restart
-- **Description:** Restart local Chainlink node
-
-#### Stop Chainlink node
-
-- **Method:** stop
-- **Description:** Stop local Chainlink node
-
-#### Get ETH keys
-
-- **Method:** getETHKeys
-- **Description:** Get list of Chainlink node's ETH keys
-
-#### Get P2P keys
-
-- **Method:** getP2PKeys
-- **Description:** Get list of Chainlink node's P2P keys
-
-#### Get OCR keys
-
-- **Method:** getOCRKeys
-- **Description:** Get list of Chainlink node's OCR keys
-
-#### Get jobs
-
-- **Method:** getJobs
-- **Description:** Get list of Chainlink node's jobs
-
-#### Create Direct Request job
-
-- **Method:** createDirectRequestJob
-- **Description:** Set up Direct Request job for local Chainlink node
-- **Arguments:**
- - `operatorAddress`: Operator contract address
-
-Check Direct Request job configuration template: [direct-request-job_template.toml](src%2Fsandbox%2Fnode%2Fsetup%2Fclroot%2Fjobs%2Fdirect-request-job_template.toml).
-
-### Service alias: `linkToken`
-
-This section provides methods and functionalities designed to interact with the [Link Token](contracts%2FLinkToken.sol).
-
-### Methods
-
-#### Deploy contract
-
-- **Method:** deploy
-- **Description:** Deploy Link Token contract
-
-#### Transfer
-
-- **Method:** transfer
-- **Description:** Transfer Link Tokens to recipient
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
- - `recipient`: Account to which Link Tokens will be transferred
- - `amount`: Amount of Link Tokens to be transferred
-
-#### Get allowance
-
-- **Method:** getAllowance
-- **Description:** Get Link Token allowance
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
- - `owner`: Link Tokens owner
- - `spender`: Owner's Link Tokens spender
-
-#### Increase approval
-
-- **Method:** increaseApproval
-- **Description:** Increase Link Token approval
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
- - `spender`: Account for which Link Token approval will be increased
- - `addedValue`: Amount of Link Tokens to be added
-
-#### Decrease approval
-
-- **Method:** decreaseApproval
-- **Description:** Decrease Link Token approval
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
- - `spender`: Account for which Link Token approval will be decreased
- - `subtractedValue`: Amount of Link Tokens to be decreased
-
-### Service alias: `operator`
-
-This section provides methods and functionalities designed to interact with the [Operator](contracts%2FOperator.sol).
-
-### Methods
-
-#### Deploy contract
-
-- **Method:** deploy
-- **Description:** Deploy Operator contract
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
-
-#### Set authorized sender
-
-- **Method:** setAuthorizedSender
-- **Description:** Set authorized sender
-- **Arguments:**
- - `operatorAddress`: Operator address
- - `sender`: Address to be authorized
-
-### Service alias: `drConsumer`
-
-This section provides methods and functionalities designed to interact with the [Direct Request Consumer](contracts%2FChainlinkDirectRequestConsumer.sol).
-
-### Methods
-
-#### Deploy contract
-
-- **Method:** deploy
-- **Description:** Deploy Direct Request Consumer contract
-- **Arguments:**
- - `linkTokenAddress`: Link Token address
-
-#### Request data
-
-- **Method:** requestData
-- **Description:** Request data to be fulfilled with Direct Request job
-- **Arguments:**
- - `directRequestConsumerAddress`: Direct Request Consumer address
- - `operatorAddress`: Operator address
- - `externalJobID`: Direct Request External Job ID
- - `observationURL`: URL to retrieve data
- - `pathToData`: JSON path to data in observation URL response, e.g. "ethereum,usd"
- - `multiplyTimes`: Multiplier for the received answer
-
-#### Get latest answer
-
-- **Method:** getLatestAnswer
-- **Description:** Get latest answer
-- **Arguments:**
- - `directRequestConsumerAddress`: Direct Request Consumer address
-
+> **Note**
+> The following are methods that are available only in HRE
+
+#### Build CBOR for Functions request
+
+- **Method:** buildFunctionsRequestCBOR
+- **Description:** Build CBOR for Functions request
+- **Arguments:** `(codeLocation: number, codeLanguage: number, source: string, secretsLocation: number, encryptedSecretsReference: string, args: string[], bytesArgs: string[])`
+ - `codeLocation`: Inline = 0 | Remote = 1 | DONHosted = 2
+ - `codeLanguage`: JavaScript = 0
+ - `source`: Functions request source code
+ - `secretsLocation`: Inline = 0 | Remote = 1 | DONHosted = 2
+ - `encryptedSecretsReference`: HEX string, result of buildDONHostedEncryptedSecretsReference method
+ - `args`: Functions request args
+ - `bytesArgs`: Functions request bytes args
+- **Returns:** `(cbor: string)`
+ - `cbor`: HEX string of CBOR
+- **Usage:**
+ ```typescript
+ const codeLocation = 0;
+ const codeLanguage = 0;
+ const source = 'return true;';
+ const secretsLocation = 0;
+ const encryptedSecretsReference = '0x89630569c9567e43c4fe7b1633258df9f2531b62f2352fa721cf3162ee4ecb46';
+ const args = [];
+ const bytesArgs = [];
+ const cbor = await utils.buildFunctionsRequestCBOR(codeLocation, codeLanguage, source, secretsLocation, encryptedSecretsReference, args, bytesArgs);
+ ```
+
+#### Decode HEX String
+
+- **Method:** decodeHexString
+- **Description:** Decode HEX String
+- **Arguments:** `(resultHexString: string, expectedReturnType: ReturnType)`
+ - `resultHexString`: HEX string to decode
+ - `expectedReturnType`: ReturnType
+- **Returns:** `(result: DecodedResult)`
+ - `result`: Decoded result
+- **Usage:**
+ ```typescript
+ const resultHexString = '0x89630569c9567e43c4fe7b1633258df9f2531b62f2352fa721cf3162ee4ecb46';
+ const expectedReturnType = ReturnType.string;
+ const result = await utils.decodeHexString(resultHexString, expectedReturnType);
+ ```
diff --git a/README.md b/README.md
index 56a2cde..6bcdd04 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,22 @@
+# Hardhat Chainlink Plugin
+
+
-
-# Hardhat Chainlink Plugin
+
The Hardhat Chainlink plugin allows users to seamlessly interact with Chainlink services in their Hardhat-based projects.
-It provides atomic methods to interact with smart contracts related to the main Chainlink services: Data Feeds, VRF, and Automation.
+It provides atomic methods to interact with smart contracts related to the following Chainlink services: Data Feeds, VRF, Automation and Functions.
This plugin offers a convenient way to integrate Chainlink functionality into your web3 development workflow.
> **Warning**
>
> **This package is currently in the BETA testing phase and is not recommended for production usage yet.**
>
-> **Open issues to submit bugs and earn the Beta Tester POAP.**
+> **Open issues to submit bugs.**
## Installation
> **Note**
@@ -46,22 +48,34 @@ require("@chainlink/hardhat-chainlink");
import "@chainlink/hardhat-chainlink";
```
-This plugin also extends the Hardhat configuration and adds `chainlink` parameters group:
-`hardhat.config.ts`:
+## Usage
+
+The Hardhat Chainlink plugin offers multiple ways to interact with Chainlink services,
+giving you the flexibility to choose the approach that suits your workflow best.
+
+Below is a mapping of the names of supported Chainlink services:
+- `dataFeed`: Data Feeds
+- `dataFeedProxy`: Data Feed Proxies
+- `feedRegistry`: Feed Registries
+- `l2Sequencer`: L2 Sequencers
+- `ens`: ENS (Ethereum Name Service)
+- `automationRegistry`: Automation Registries
+- `automationRegistrar`: Automation Registrars
+- `vrf`: VRF (Verifiable Random Functions)
+- `functions`: Functions service
+
+The number of confirmations to wait for transactions to Chainlink services can be set using
+the corresponding parameter in the `chainlink` parameters group of `hardhat.config.ts`:
```ts
module.exports = {
+ ...,
chainlink: {
- confirmations // Number of confirmations to wait for transactions, default: 1
+ confirmations // Number of confirmations to wait for transactions (default: 1)
},
...
}
```
-## Usage
-
-The Hardhat Chainlink plugin offers multiple ways to interact with Chainlink services,
-giving you the flexibility to choose the approach that suits your workflow best.
-
### 1. CLI
Interact with the Hardhat Chainlink plugin through the command line interface (CLI) using the following format:
@@ -132,19 +146,6 @@ async function myFunction() {
---
Choose the method that fits your project's requirements and coding style.
All three approaches provide the same set of functionalities, allowing you to interact with Chainlink services efficiently and effectively.
-
-### Available Services
-The Hardhat Chainlink plugin supports the following Chainlink services:
-
-- `dataFeed` (Data Feeds)
-- `dataFeedProxy` (Data Feed Proxies)
-- `feedRegistry` (Feed Registries)
-- `l2Sequencer` (L2 Sequencers)
-- `ens` (ENS - Ethereum Name Service)
-- `automationRegistry` (Automation Registries)
-- `automationRegistrar` (Automation Registrars)
-- `vrf` (Verifiable Random Functions)
-
For a more in-depth understanding of available services and methods, please explore their [tests](test).
## Registries
@@ -155,11 +156,22 @@ which is useful for interacting with the Feed Registry.
In general, these registries help you access essential contract addresses deployed on different networks,
making it easier to integrate Chainlink services into your projects.
-You can access the plugin registries using one of the following methods:
+Below is a list of the objects containing address data for service configurations for the Chainlink services
+provided by the Hardhat Chainlink plugin:
+- `dataFeeds`: Addresses of Data Feeds-related contracts: Aggregators and Proxies, and their parameters.
+- `feedRegistries`: Feed Registries' contract addresses.
+- `l2Sequencers`: L2 Sequencer Uptime Feeds' contract addresses.
+- `keeperRegistries`: Addresses of Automation-related contracts: Keeper Registry and Keeper Registrar.
+- `linkTokens`: Link Tokens' contract addresses.
+- `vrfCoordinators`: Addresses of VRF Coordinators and their parameters.
+- `functionsRouters`: Addresses of Functions Routers and their parameters.
+- `denominations`: Records from Denominations library to interact with Feed Registries contracts.
+
+You can access them using one of the following methods:
### 1. CLI
-To interact with the registries through the CLI, use the following command:
+Access the registries through the CLI using the following command:
```
npx hardhat chainlink:registries [method]
```
@@ -187,7 +199,7 @@ Access the registries as methods directly in the Hardhat Environment:
const registry = hre.chainlink.registries.{registryName};
```
-Replace {registryName} with the name of the registry (e.g., dataFeeds, feedRegistries, keeperRegistries).
+Replace the `{registryName}` placeholder with the name of the registry (e.g., dataFeeds, feedRegistries, keeperRegistries).
Example of getting data from registry in the Hardhat Environment:
```js
@@ -197,49 +209,48 @@ async function myFunction() {
// 0xE62B71cf983019BFf55bC83B48601ce8419650CC
}
```
+---
+For a more in-depth understanding of the structure of these records, please explore their [interfaces](src%2Fregistries%2Finterfaces).
-### Available registries
+## Sandbox
-The Hardhat Chainlink plugin provides the following registries:
-- `dataFeeds`: Addresses of Data Feeds-related contracts: Aggregators and Proxies, and their parameters.
-- `feedRegistries`: Feed Registries' contract addresses.
-- `l2Sequencers`: L2 Sequencer Uptime Feeds' contract addresses.
-- `keeperRegistries`: Addresses of Automation-related contracts: Keeper Registry and Keeper Registrar.
-- `linkTokens`: Link Tokens' contract addresses.
-- `vrfCoordinators`: Addresses of VRF Coordinators and their parameters.
-- `denominations`: Records from Denominations library to interact with Feed Registries contracts.
+The `sandbox` module of Hardhat Chainlink plugin provides the ability to test dApps against Chainlink services locally and run simulations.
-For a more in-depth understanding of the structure of these records, please explore their [interfaces](src%2Fregistries%2Finterfaces).
+### Functions request simulation
-## Sandbox
+This plugin enables you to run local Functions request simulations.
+A simulation is an execution of your custom JavaScript code in a locally spun up Deno sandbox environment.
+It is useful for debugging and for checking whether the source code you supply to Chainlink Functions can reasonably be expected to work when passed on-chain.
-In addition to the primary feature of interacting with Chainlink services, this plugin provides the ability to manage a local Chainlink node.
-The corresponding functionality is implemented in a special tasks module `sandbox`.
-This module implements methods for starting, restarting and stopping a Chainlink node, getting Chainlink node information,
-deploying and interacting with such contracts as [LinkToken](contracts%2FLinkToken.sol), [Operator](contracts%2FOperator.sol) and [ChainlinkDirectRequestConsumer](contracts%2FChainlinkDirectRequestConsumer.sol).
+> **Note**
+Install [Deno](https://deno.com/) and add it to PATH, run ```deno --version``` to verify installation. Instructions: [https://deno.com/#installation](https://deno.com/#installation).
-### Configure, run and manage local Chainlink node
+Functions' requests simulations could be performed following the [sandbox documentation](SANDBOX.md#service-alias-functionssimulation).
-This plugin allows you to run a local Chainlink node and then manage it using Docker.
+### Local testing
> **Note**
-Install and run Docker Daemon, and Docker Desktop for convenience. Instructions: [docs.docker.com/get-docker](https://docs.docker.com/get-docker/).
+> Install and run Docker Daemon, and Docker Desktop for convenience. Instructions: [docs.docker.com/get-docker](https://docs.docker.com/get-docker/).
-Before you start a Chainlink node, it's important to configure it. To achieve this, parameters have been included in the Hardhat configuration `chainlink` group:
-`hardhat.config.ts`:
+This plugin enables you to spin up a local Chainlink node, set up Chainlink services, and then conduct local tests.
+
+#### Configure local Chainlink node
+
+Before you start a Chainlink node, it's important to configure it.
+To achieve this, additional parameters have been included in the `chainlink` group of `hardhat.config.ts`:
```ts
module.exports = {
chainlink: {
node: {
- chain_id, // Chain ID, default: "1337"
- chain_name, // Chain name, default: "local"
- http_url, // JSON RPC HTTP endpoint, default: "http://host.docker.internal:8545"
- ws_url, // JSON RPC WebSocket endpoint, default: "ws://host.docker.internal:8545"
- cl_keystore_password, // Password to encode Chainlink keys in database, default: "password1234567890"
- cl_api_user, // Email of Chainlink API user/admin, default: "user@chain.link"
- cl_api_password, // Password of Chainlink API user/admin, default: "password1234567890"
- pg_user, // Postgres DB user name, default: "chainlink"
- pg_password, // Postgres DB user password, default: "password1234567890"
- pg_db, // Postgres DB name, default: "chainlink"
+ chain_id, // Chain ID (default: "1337")
+ chain_name, // Chain name (default: "local")
+ http_url, // JSON RPC HTTP endpoint (default: "http://host.docker.internal:8545")
+ ws_url, // JSON RPC WebSocket endpoint (default: "ws://host.docker.internal:8545")
+ cl_keystore_password, // Password to encode Chainlink keys in database (default: "password1234567890")
+ cl_api_user, // Email of Chainlink API user/admin (default: "user@chain.link")
+ cl_api_password, // Password of Chainlink API user/admin (default: "password1234567890")
+ pg_user, // Postgres DB user name (default: "chainlink")
+ pg_password, // Postgres DB user password (default: "password1234567890")
+ pg_db, // Postgres DB name (default: "chainlink")
}
},
...
@@ -248,11 +259,15 @@ module.exports = {
> **Note**
> Passwords must contain both letters and numbers and be at least 16 characters long.
-Once these parameters are specified, Chainlink node could be started/restarted/stopped and managed with plugin following the [documentation](DOCUMENTATION.md#service-alias-node).
+### Manage local Chainlink node
-You can also manage a Chainlink node either with Chainlink CLI or Chainlink node GUI.
+Once local Chainlink node parameters are specified, Chainlink node could be started/restarted/stopped and managed with the plugin following the [sandbox documentation](SANDBOX.md#service-alias-node).
+In order to login use credentials provided with `cl_api_user` and `cl_api_password`.
+
+Alternatively, you can manage a Chainlink node either with Chainlink CLI or Chainlink node GUI.
#### Chainlink CLI
+
Chainlink node CLI is available directly on a machine running Chainlink node, so first you have to connect with `bash` to a Docker container to be able to run commands.
Here are some of the things you can do with the CLI:
@@ -262,47 +277,32 @@ Here are some of the things you can do with the CLI:
* See/Create Chainlink node's keys: ETH, OCR, P2P
* and more...
-Here is example command to get list of ETH keys that are used by the Chainlink node:
+Here is example command to get list of ETH keys that are used by a Chainlink node:
```bash
chainlink keys eth list
```
-The most useful commands to manage Chainlink node with UI you can find here: https://docs.chain.link/chainlink-nodes/resources/miscellaneous.
+The most useful commands to manage Chainlink node with CLI you can find here: https://docs.chain.link/chainlink-nodes/resources/miscellaneous.
#### Chainlink GUI
+
Chainlink node GUI is by default available on the port `6688`, this port is exposed with a docker-compose file to a host machine.
Here are some of the things you can do with the GUI:
* Create/Delete Chainlink Jobs
* Create Chainlink Bridge
* See Chainlink Jobs runs
-* See Chainlink node's keys: ETH, OCR, P2P
+* See Chainlink node's keys: ETH, OCR, P2P, VRF
* See Chainlink node's current configuration
* and more...
-In order to login use credentials provided with `cl_api_user` and `cl_api_password`.
-
-### Set up a Direct Request job with plugin
-
-Once Chainlink node is started, Direct Request job could be set up. It can be used for testing, learning and other purposes.
-The process of setting up a Direct Request job is as follows:
-1. [Deploy Link Token contract](DOCUMENTATION.md#deploy-contract)
-2. [Deploy Operator contract](DOCUMENTATION.md#deploy-contract-1)
-3. [Deploy Direct Request Consumer contract](DOCUMENTATION.md#deploy-contract-1)
-4. [Get Chainlink node ETH accounts](DOCUMENTATION.md#get-eth-keys) and choose one of them
-5. [Fund chosen Chainlink ETH account](DOCUMENTATION.md#transfer-eth) with reasonable amount of ETH
-6. [Fund Direct Request Consumer contract with Link tokens](DOCUMENTATION.md#transfer) (at least 1 token)
-7. [Set chosen Chainlink ETH account to Operator contract as Authorized Sender](DOCUMENTATION.md#set-authorized-sender)
-8. [Create Direct Request job](DOCUMENTATION.md#create-direct-request-job)
-9. [Request data with Direct Request job](DOCUMENTATION.md#request-data)
-10. [Check if answer in Direct Request consumer was updated](DOCUMENTATION.md#get-latest-answer)
-
-More on Direct Request job: https://docs.chain.link/chainlink-nodes/oracle-jobs/all-jobs#direct-request-jobs.
-
## Documentation
-For detailed usage instructions and more information on each service and method, refer to the [DOCUMENTATION.md](DOCUMENTATION.md).
+For detailed usage instructions and more information, refer to:
+* [DOCUMENTATION.md](DOCUMENTATION.md) for interaction with Chainlink services;
+* [SANDBOX.md](SANDBOX.md) for local testing in Sandbox.
## Contribution
+
We welcome contributions from the community.
If you find any issues, have suggestions for improvements, or want to add new features to the plugin,
diff --git a/SANDBOX.md b/SANDBOX.md
new file mode 100644
index 0000000..8702580
--- /dev/null
+++ b/SANDBOX.md
@@ -0,0 +1,358 @@
+# Sandbox Documentation
+This document provides a description of the suite of services available in the 'Sandbox' module (alias: sandbox) of the Hardhat Chainlink plugin.
+These services facilitate spinning up and managing a Chainlink node, as well as configuring Chainlink jobs.
+
+
+* [Sandbox Documentation](#sandbox-documentation)
+ * [Service alias: `functionsSimulation`](#service-alias-functionssimulation)
+ * [Simulate Functions Request](#simulate-functions-request)
+ * [Service alias: `node`](#service-alias-node)
+ * [Run Chainlink node](#run-chainlink-node)
+ * [Restart Chainlink node](#restart-chainlink-node)
+ * [Stop Chainlink node](#stop-chainlink-node)
+ * [Get ETH keys](#get-eth-keys)
+ * [Get P2P keys](#get-p2p-keys)
+ * [Get OCR keys](#get-ocr-keys)
+ * [Get jobs](#get-jobs)
+ * [Create Direct Request job](#create-direct-request-job)
+ * [Service alias: `linkToken`](#service-alias-linktoken)
+ * [Deploy contract](#deploy-contract)
+ * [Transfer](#transfer)
+ * [Get allowance](#get-allowance)
+ * [Increase approval](#increase-approval)
+ * [Decrease approval](#decrease-approval)
+ * [Service alias: `operator`](#service-alias-operator)
+ * [Deploy contract](#deploy-contract-1)
+ * [Set authorized sender](#set-authorized-sender)
+ * [Service alias: `directRequestConsumer`](#service-alias-directrequestconsumer)
+ * [Deploy contract](#deploy-contract-2)
+ * [Request data](#request-data)
+ * [Get latest answer](#get-latest-answer)
+ * [Set up a Direct Request job with plugin](#set-up-a-direct-request-job-with-plugin)
+
+
+### Service alias: [`functionsSimulation`](src%2Fsandbox%2FfunctionsSimulations%2Findex.ts)
+```typescript
+const functionsSimulation = hre.chainlink.sandbox.functionsSimulation;
+```
+This section provides methods and functionalities designed to run Functions request simulations.
+
+#### Simulate Functions Request
+
+- **Method:** simulateRequest
+- **Description:** Simulate Functions request
+- **Arguments:** `(source: string, args: string[], bytesArgs: string[])`
+ - `source`: Source code to execute
+ - `args`: Request args
+ - `bytesArgs`: Request bytes args
+- **Returns:** `(decodedResult: DecodedResult)`
+ - `decodedResult`: Decoded result of the request
+- **Usage:**
+ ```typescript
+ const source = `
+ return 0
+ `;
+ const args = [];
+ const bytesArgs = [];
+ const decodedResult = await functionsSimulation.simulateRequest(source, args, bytesArgs);
+ ```
+
+### Service alias: [`node`](src%2Fsandbox%2Fnode%2Findex.ts)
+```typescript
+const node = hre.chainlink.sandbox.node;
+```
+This section provides methods and functionalities designed to spin up and manage a Chainlink node.
+
+#### Run Chainlink node
+
+- **Method:** run
+- **Description:** Run local Chainlink node
+- **Usage:**
+ ```typescript
+ await node.run();
+ ```
+
+#### Restart Chainlink node
+
+- **Method:** restart
+- **Description:** Restart local Chainlink node
+- **Usage:**
+ ```typescript
+ await node.restart();
+ ```
+
+#### Stop Chainlink node
+
+- **Method:** stop
+- **Description:** Stop local Chainlink node
+- **Usage:**
+ ```typescript
+ await node.stop();
+ ```
+
+#### Get ETH keys
+
+- **Method:** getETHKeys
+- **Description:** Get list of Chainlink node's ETH keys
+- **Returns:** `(ethKeys: string)`
+ - `ethKeys`: List of Chainlink node's ETH keys
+- **Usage:**
+ ```typescript
+ const ethKeys = await node.getETHKeys();
+ ```
+
+#### Get P2P keys
+
+- **Method:** getP2PKeys
+- **Description:** Get list of Chainlink node's P2P keys
+- **Returns:** `(p2pKeys: string)`
+ - `p2pKeys`: List of Chainlink node's P2P keys
+- **Usage:**
+ ```typescript
+ const p2pKeys = await node.getP2PKeys();
+ ```
+
+#### Get OCR keys
+
+- **Method:** getOCRKeys
+- **Description:** Get list of Chainlink node's OCR keys
+- **Returns:** `(ocrKeys: string)`
+ - `ocrKeys`: List of Chainlink node's OCR keys
+- **Usage:**
+ ```typescript
+ const ocrKeys = await node.getOCRKeys();
+ ```
+
+#### Get jobs
+
+- **Method:** getJobs
+- **Description:** Get list of Chainlink node's jobs
+- **Returns:** `(jobs: string)`
+ - `jobs`: List of Chainlink node's jobs
+- **Usage:**
+ ```typescript
+ const jobs = await node.getJobs();
+ ```
+
+#### Create Direct Request job
+
+- **Method:** createDirectRequestJob
+- **Description:** Set up Direct Request job for local Chainlink node
+- **Arguments:** `(operatorAddress: string)`
+ - `operatorAddress`: Operator contract address
+- **Usage:**
+ ```typescript
+ const operatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ await node.createDirectRequestJob(operatorAddress);
+ ```
+
+### Service alias: [`linkToken`](src%2Fsandbox%2FlinkToken%2Findex.ts)
+```typescript
+const linkToken = hre.chainlink.sandbox.linkToken;
+```
+This section provides methods and functionalities designed to interact with the [Link Token](contracts%2FLinkToken.sol).
+
+#### Deploy contract
+
+- **Method:** deploy
+- **Description:** Deploy Link Token contract
+- **Returns:** `(linkTokenAddress: string)`
+ - `linkTokenAddress`: Link Token address
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = await linkToken.deploy();
+ ```
+
+#### Transfer
+
+- **Method:** transfer
+- **Description:** Transfer Link Tokens to recipient
+- **Arguments:** `(linkTokenAddress: string, recipient: string, amount: BigNumberish)`
+ - `linkTokenAddress`: Link Token address
+ - `recipient`: Account to which Link Tokens will be transferred
+ - `amount`: Amount of Link Tokens to be transferred
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that transferred the funds
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const recipient = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const amount = ethers.utils.parseEther('100');
+ const txData = await linkToken.transfer(linkTokenAddress, recipient, amount);
+ ```
+
+#### Get allowance
+
+- **Method:** getAllowance
+- **Description:** Get Link Token allowance
+- **Arguments:** `(linkTokenAddress: string, owner: string, spender: string)`
+ - `linkTokenAddress`: Link Token address
+ - `owner`: Link Tokens owner
+ - `spender`: Owner's Link Tokens spender
+- **Returns:** `(allowance: BigNumber)`
+ - `allowance`: Link Token allowance
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const owner = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const spender = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const allowance = await linkToken.getAllowance(linkTokenAddress, owner, spender);
+ ```
+
+#### Increase approval
+
+- **Method:** increaseApproval
+- **Description:** Increase Link Token approval
+- **Arguments:** `(linkTokenAddress: string, spender: string, addedValue: BigNumberish)`
+ - `linkTokenAddress`: Link Token address
+ - `spender`: Account for which Link Token approval will be increased
+ - `addedValue`: Amount of Link Tokens to be added
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that increased the approval
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const spender = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const addedValue = '10000000000';
+ const txData = await linkToken.increaseApproval(linkTokenAddress, spender, addedValue);
+ ```
+
+#### Decrease approval
+
+- **Method:** decreaseApproval
+- **Description:** Decrease Link Token approval
+- **Arguments:** `(linkTokenAddress: string, spender: string, subtractedValue: BigNumberish)`
+ - `linkTokenAddress`: Link Token address
+ - `spender`: Account for which Link Token approval will be decreased
+ - `subtractedValue`: Amount of Link Tokens to be decreased
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that decreased the approval
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const spender = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const subtractedValue = '10000000000';
+ const txData = await linkToken.decreaseApproval(linkTokenAddress, spender, subtractedValue);
+ ```
+
+### Service alias: [`operator`](src%2Fsandbox%2Foperator%2Findex.ts)
+```typescript
+const operator = hre.chainlink.sandbox.operator;
+```
+This section provides methods and functionalities designed to interact with the [Operator](contracts%2FOperator.sol).
+
+#### Deploy contract
+
+- **Method:** deploy
+- **Description:** Deploy Operator contract
+- **Arguments:** `(linkTokenAddress: string)`
+ - `linkTokenAddress`: Link Token address
+- **Returns:** `(operatorAddress: string)`
+ - `operatorAddress`: Operator address
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const operatorAddress = await operator.deploy(linkTokenAddress);
+ ```
+
+#### Set authorized sender
+
+- **Method:** setAuthorizedSender
+- **Description:** Set authorized sender
+- **Arguments:** `(operatorAddress: string, sender: string)`
+ - `operatorAddress`: Operator address
+ - `sender`: Address to be authorized
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that set the authorized sender
+- **Usage:**
+ ```typescript
+ const operatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const sender = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const txData = await operator.setAuthorizedSender(operatorAddress, sender);
+ ```
+
+### Service alias: [`directRequestConsumer`](src%2Fsandbox%2FdirectRequestConsumer%2Findex.ts)
+```typescript
+const directRequestConsumer = hre.chainlink.sandbox.directRequestConsumer;
+```
+This section provides methods and functionalities designed to interact with the [Direct Request Consumer](contracts%2FChainlinkDirectRequestConsumer.sol).
+
+#### Deploy contract
+
+- **Method:** deploy
+- **Description:** Deploy Direct Request Consumer contract
+- **Arguments:** `(linkTokenAddress: string)`
+ - `linkTokenAddress`: Link Token address
+- **Returns:** `(directRequestConsumerAddress: string)`
+ - `directRequestConsumerAddress`: Direct Request Consumer address
+- **Usage:**
+ ```typescript
+ const linkTokenAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709';
+ const directRequestConsumerAddress = await directRequestConsumer.deploy(linkTokenAddress);
+ ```
+
+#### Request data
+
+- **Method:** requestData
+- **Description:** Request data to be fulfilled with Direct Request job
+- **Arguments:** `(directRequestConsumerAddress: string, operatorAddress: string, externalJobID: string, observationURL: string, pathToData: string, multiplyTimes: string)`
+ - `directRequestConsumerAddress`: Direct Request Consumer address
+ - `operatorAddress`: Operator address
+ - `externalJobID`: Direct Request External Job ID
+ - `observationURL`: URL to retrieve data
+ - `pathToData`: JSON path to data in observation URL response, e.g. "ethereum,usd"
+ - `multiplyTimes`: Multiplier for the received answer
+- **Returns:** `(txData: {
+ transactionHash: string;
+})`
+ - `transactionHash`: Transaction hash of the transaction that requested data
+- **Usage:**
+ ```typescript
+ const directRequestConsumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const operatorAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const externalJobID = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const observationURL = 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD';
+ const pathToData = 'USD';
+ const multiplyTimes = '100';
+ const txData = await directRequestConsumer.requestData(directRequestConsumerAddress, operatorAddress, externalJobID, observationURL, pathToData, multiplyTimes);
+ ```
+
+#### Get latest answer
+
+- **Method:** getLatestAnswer
+- **Description:** Get latest answer
+- **Arguments:** `(directRequestConsumerAddress: string)`
+ - `directRequestConsumerAddress`: Direct Request Consumer address
+- **Returns:** `(latestAnswer: BigNumber)`
+ - `latestAnswer`: Latest answer
+- **Usage:**
+ ```typescript
+ const directRequestConsumerAddress = '0x8A753747A1Fa494EC906cE90E9f37563A8AF630e';
+ const latestAnswer = await directRequestConsumer.getLatestAnswer(directRequestConsumerAddress);
+ ```
+
+### Set up a Direct Request job with plugin
+
+Once Chainlink node is started, Direct Request job could be set up. It can be used for testing, learning and other purposes.
+The process of setting up a Direct Request job is as follows:
+1. [Deploy Link Token contract](SANDBOX.md#deploy-contract)
+2. [Deploy Operator contract](SANDBOX.md#deploy-contract-1)
+3. [Deploy Direct Request Consumer contract](SANDBOX.md#deploy-contract-1)
+4. [Get Chainlink node ETH accounts](SANDBOX.md#get-eth-keys) and choose one of them
+5. [Fund chosen Chainlink ETH account](DOCUMENTATION.md#transfer-eth) with reasonable amount of ETH
+6. [Fund Direct Request Consumer contract with Link tokens](SANDBOX.md#transfer) (at least 1 token)
+7. [Set chosen Chainlink ETH account to Operator contract as Authorized Sender](SANDBOX.md#set-authorized-sender)
+8. [Create Direct Request job](SANDBOX.md#create-direct-request-job)
+9. [Request data with Direct Request job](SANDBOX.md#request-data)
+10. [Check if answer in Direct Request consumer was updated](SANDBOX.md#get-latest-answer)
+
+Check Direct Request job configuration template: [direct-request-job_template.toml](src%2Fsandbox%2Fnode%2Fsetup%2Fclroot%2Fjobs%2Fdirect-request-job_template.toml).
+More on Direct Request job: https://docs.chain.link/chainlink-nodes/oracle-jobs/all-jobs#direct-request-jobs.
diff --git a/config/typescript/tsconfig.json b/config/typescript/tsconfig.json
index 81fd394..206fae7 100644
--- a/config/typescript/tsconfig.json
+++ b/config/typescript/tsconfig.json
@@ -7,6 +7,7 @@
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
- "resolveJsonModule": true
+ "resolveJsonModule": true,
+ "skipLibCheck": true
}
}
diff --git a/contracts/ChainlinkDirectRequestConsumer.sol b/contracts/ChainlinkDirectRequestConsumer.sol
index 5b1f1f8..8a56b04 100644
--- a/contracts/ChainlinkDirectRequestConsumer.sol
+++ b/contracts/ChainlinkDirectRequestConsumer.sol
@@ -3,9 +3,9 @@
pragma solidity ^0.8.0;
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
-import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
+import "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
import "@chainlink/contracts/src/v0.8/Chainlink.sol";
-import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
+import "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
diff --git a/contracts/FunctionsConsumer.sol b/contracts/FunctionsConsumer.sol
new file mode 100644
index 0000000..7de963f
--- /dev/null
+++ b/contracts/FunctionsConsumer.sol
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.19;
+
+import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/FunctionsClient.sol";
+import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
+import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/libraries/FunctionsRequest.sol";
+
+/**
+ * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
+ * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
+ * DO NOT USE THIS CODE IN PRODUCTION.
+ */
+
+contract FunctionsConsumer is FunctionsClient, ConfirmedOwner {
+ using FunctionsRequest for FunctionsRequest.Request;
+
+ bytes32 public donId; // DON ID for the Functions DON to which the requests are sent
+
+ bytes32 public s_lastRequestId;
+ bytes public s_lastResponse;
+ bytes public s_lastError;
+
+ constructor(address router, bytes32 _donId) FunctionsClient(router) ConfirmedOwner(msg.sender) {
+ donId = _donId;
+ }
+
+ /**
+ * @notice Set the DON ID
+ * @param newDonId New DON ID
+ */
+ function setDonId(bytes32 newDonId) external onlyOwner {
+ donId = newDonId;
+ }
+
+ /**
+ * @notice Triggers an on-demand Functions request
+ * @param source JavaScript source code
+ * @param secretsLocation Location of secrets (only Location.Remote & Location.DONHosted are supported)
+ * @param encryptedSecretsReference Reference pointing to encrypted secrets
+ * @param args String arguments passed into the source code and accessible via the global variable `args`
+ * @param bytesArgs Bytes arguments passed into the source code and accessible via the global variable `bytesArgs` as hex strings
+ * @param subscriptionId Subscription ID used to pay for request (FunctionsConsumer contract address must first be added to the subscription)
+ * @param callbackGasLimit Maximum amount of gas used to call the inherited `handleOracleFulfillment` method
+ */
+ function sendRequest(
+ string calldata source,
+ FunctionsRequest.Location secretsLocation,
+ bytes calldata encryptedSecretsReference,
+ string[] calldata args,
+ bytes[] calldata bytesArgs,
+ uint64 subscriptionId,
+ uint32 callbackGasLimit
+ ) external onlyOwner {
+ FunctionsRequest.Request memory req;
+ req.initializeRequest(FunctionsRequest.Location.Inline, FunctionsRequest.CodeLanguage.JavaScript, source);
+ req.secretsLocation = secretsLocation;
+ req.encryptedSecretsReference = encryptedSecretsReference;
+ if (args.length > 0) {
+ req.setArgs(args);
+ }
+ if (bytesArgs.length > 0) {
+ req.setBytesArgs(bytesArgs);
+ }
+ s_lastRequestId = _sendRequest(req.encodeCBOR(), subscriptionId, callbackGasLimit, donId);
+ }
+
+ /**
+ * @notice Triggers an on-demand Functions request that has been encoded off-chain
+ * @param encodedRequest CBOR-encoded Functions request
+ * @param subscriptionId Subscription ID used to pay for request (FunctionsConsumer contract address must first be added to the subscription)
+ * @param callbackGasLimit Maximum amount of gas used to call the inherited `handleOracleFulfillment` method
+ */
+ function sendEncodedRequest(
+ bytes calldata encodedRequest,
+ uint64 subscriptionId,
+ uint32 callbackGasLimit
+ ) external onlyOwner {
+ s_lastRequestId = _sendRequest(encodedRequest, subscriptionId, callbackGasLimit, donId);
+ }
+
+ /**
+ * @notice Store latest result/error
+ * @param requestId The request ID, returned by sendRequest()
+ * @param response Aggregated response from the user code
+ * @param err Aggregated error from the user code or from the execution pipeline
+ * Either response or error parameter will be set, but never both
+ */
+ function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override {
+ s_lastResponse = response;
+ s_lastError = err;
+ }
+}
diff --git a/hardhat.config.ts b/hardhat.config.ts
index 0be0a7c..0bc4673 100644
--- a/hardhat.config.ts
+++ b/hardhat.config.ts
@@ -13,7 +13,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
- version: "0.8.18",
+ version: "0.8.19",
settings: commonCompilerSettings,
},
{
diff --git a/package.json b/package.json
index 3f82f53..3c1117b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@chainlink/hardhat-chainlink",
- "version": "0.0.3",
+ "version": "0.0.4",
"description": "Hardhat plugin that adds interaction with Chainlink services to Hardhat projects",
"repository": "https://github.com/smartcontractkit/hardhat-chainlink.git",
"license": "MIT",
@@ -24,7 +24,9 @@
"test:vrfCoordinator": "mocha --exit 'test/vrfCoordinator.test.ts'",
"test:automationRegistrar": "mocha --exit 'test/automationRegistrar.test.ts'",
"test:automationRegistry": "mocha --exit 'test/automationRegistry.test.ts'",
- "test": "yarn test:prepare && yarn test:dataFeed && yarn test:dataFeedProxy && yarn test:ensFeedsResolver && yarn test:feedRegistry && yarn test:l2FeedUptimeSequencer && yarn test:vrfCoordinator && yarn test:automationRegistrar && yarn test:automationRegistry",
+ "test:functions": "mocha --exit 'test/functionsRouter.test.ts'",
+ "test:functionsSimulation": "mocha --exit 'test/functionsSimulation.test.ts'",
+ "test": "yarn test:prepare && yarn test:dataFeed && yarn test:dataFeedProxy && yarn test:ensFeedsResolver && yarn test:feedRegistry && yarn test:l2FeedUptimeSequencer && yarn test:vrfCoordinator && yarn test:automationRegistrar && yarn test:automationRegistry && yarn test:functions",
"copyArtifacts": "copyfiles -a -f ./node_modules/@chainlink/contracts/abi/**/* chainlink-artifacts",
"removeArtifacts": "rm -rf chainlink-artifacts",
"compile": "hardhat compile",
@@ -52,7 +54,7 @@
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"copyfiles": "^2.4.1",
- "hardhat": "^2.16.0",
+ "hardhat": "^2.17.3",
"hardhat-gas-reporter": "^1.0.9",
"mocha": "^10.2.0",
"mocha-suppress-logs": "^0.3.1",
@@ -66,7 +68,8 @@
"typescript": "^5.1.3"
},
"dependencies": {
- "@chainlink/contracts": "0.6.1",
+ "@chainlink/contracts": "0.8.0",
+ "@chainlink/functions-toolkit": "0.2.6",
"@inquirer/confirm": "^2.0.6",
"@inquirer/input": "^1.2.5",
"@inquirer/select": "^1.2.5",
@@ -77,6 +80,6 @@
"ethers": "^5.4.7"
},
"peerDependencies": {
- "hardhat": "^2.16.0"
+ "hardhat": "^2.17.3"
}
}
diff --git a/src/HardhatChainlink.ts b/src/HardhatChainlink.ts
index f2d226e..2b47be5 100644
--- a/src/HardhatChainlink.ts
+++ b/src/HardhatChainlink.ts
@@ -1,3 +1,4 @@
+import * as functionsToolkit from "@chainlink/functions-toolkit";
import "@nomiclabs/hardhat-ethers";
import { BigNumber, BigNumberish, BytesLike } from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
@@ -9,12 +10,19 @@ import * as dataFeedProxy from "./feeds/dataFeedProxy";
import * as ensFeedsResolver from "./feeds/ensFeedsResolver";
import * as feedRegistry from "./feeds/feedRegistry";
import * as l2FeedUptimeSequencer from "./feeds/l2FeedUptimeSequencer";
+import * as functions from "./functions";
import * as registries from "./registries";
-import * as drConsumer from "./sandbox/drConsumer";
+import * as directRequestConsumer from "./sandbox/directRequestConsumer";
+import * as functionsSimulations from "./sandbox/functionsSimulations";
import * as linkToken from "./sandbox/linkToken";
import * as node from "./sandbox/node";
import * as operator from "./sandbox/operator";
-import { DockerOutput } from "./shared/types";
+import {
+ DockerOutput,
+ FunctionsSubscriptionDetails,
+ Overrides,
+ VRFSubscriptionDetails,
+} from "./shared/types";
import * as utils from "./utils";
import * as vrf from "./vrf";
@@ -24,7 +32,7 @@ export class HardhatChainlink {
l2Sequencers: typeof registries.l2SequencersRegistry;
vrfCoordinators: typeof registries.vrfCoordinatorsRegistry;
keeperRegistries: typeof registries.keeperRegistriesRegistry;
- functionOracles: typeof registries.functionOraclesRegistry;
+ functionsRouters: typeof registries.functionsRoutersRegistry;
linkTokens: typeof registries.linkTokensRegistry;
networks: typeof registries.networksRegistry;
denominations: typeof registries.denominationsRegistry;
@@ -37,7 +45,7 @@ export class HardhatChainlink {
public vrf: VRF;
public automationRegistrar: AutomationRegistrar;
public automationRegistry: AutomationRegistry;
- public functionOracle: FunctionOracle;
+ public functions: Functions;
public utils: Utils;
public sandbox: Sandbox;
private hre: HardhatRuntimeEnvironment;
@@ -49,7 +57,7 @@ export class HardhatChainlink {
l2Sequencers: registries.l2SequencersRegistry,
vrfCoordinators: registries.vrfCoordinatorsRegistry,
keeperRegistries: registries.keeperRegistriesRegistry,
- functionOracles: registries.functionOraclesRegistry,
+ functionsRouters: registries.functionsRoutersRegistry,
linkTokens: registries.linkTokensRegistry,
networks: registries.networksRegistry,
denominations: registries.denominationsRegistry,
@@ -62,7 +70,7 @@ export class HardhatChainlink {
this.vrf = new VRF(this.hre);
this.automationRegistrar = new AutomationRegistrar(this.hre);
this.automationRegistry = new AutomationRegistry(this.hre);
- this.functionOracle = new FunctionOracle(this.hre);
+ this.functions = new Functions(this.hre);
this.utils = new Utils(this.hre);
this.sandbox = new Sandbox(this.hre);
}
@@ -71,14 +79,16 @@ export class HardhatChainlink {
class Sandbox {
public node: Node;
public operator: Operator;
- public drConsumer: DRConsumer;
+ public directRequestConsumer: DirectRequestConsumer;
public linkToken: LinkToken;
+ public functionsSimulation: FunctionsSimulation;
constructor(hre: HardhatRuntimeEnvironment) {
this.node = new Node(hre);
this.operator = new Operator(hre);
- this.drConsumer = new DRConsumer(hre);
+ this.directRequestConsumer = new DirectRequestConsumer(hre);
this.linkToken = new LinkToken(hre);
+ this.functionsSimulation = new FunctionsSimulation(hre);
}
}
@@ -593,13 +603,13 @@ class VRF {
public async cancelSubscription(
vrfCoordinatorAddress: string,
subscriptionId: BigNumberish,
- receivingWallet: string
+ receivingAddress: string
): Promise<{ transactionHash: string }> {
return vrf.cancelSubscription(
this.hre,
vrfCoordinatorAddress,
subscriptionId,
- receivingWallet
+ receivingAddress
);
}
@@ -651,12 +661,7 @@ class VRF {
public async getSubscriptionDetails(
vrfCoordinatorAddress: string,
subscriptionId: BigNumberish
- ): Promise<{
- balance: BigNumber;
- reqCount: BigNumber;
- owner: string;
- consumers: string[];
- }> {
+ ): Promise {
return vrf.getSubscriptionDetails(
this.hre,
vrfCoordinatorAddress,
@@ -905,7 +910,7 @@ class AutomationRegistry {
executeGas: number;
checkData: BytesLike;
balance: BigNumber;
- lastAutomationNode: string | undefined;
+ lastKeeper: string | undefined;
admin: string;
maxValidBlocknumber: BigNumber;
amountSpent: BigNumber;
@@ -1008,12 +1013,377 @@ class AutomationRegistry {
}
}
-class FunctionOracle {
+class Functions {
private hre: HardhatRuntimeEnvironment;
constructor(hre: HardhatRuntimeEnvironment) {
this.hre = hre;
}
+
+ public initializeSubscriptionManager(
+ functionsRouterAddress: string,
+ linkTokenAddress: string,
+ overrides?: Overrides
+ ): Promise {
+ return functions.FunctionsSubscriptionManager.initialize({
+ hre: this.hre,
+ functionsRouterAddress,
+ linkTokenAddress,
+ overrides,
+ });
+ }
+
+ public initializeResponseListener(
+ functionsRouterAddress: string,
+ overrides?: Overrides
+ ): Promise {
+ return functions.FunctionsResponseListener.initialize({
+ hre: this.hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ }
+
+ public initializeSecretsManager(
+ functionsRouterAddress: string,
+ donId: string,
+ overrides?: Overrides
+ ): Promise {
+ return functions.FunctionsSecretsManager.initialize({
+ hre: this.hre,
+ functionsRouterAddress,
+ donId,
+ overrides,
+ });
+ }
+
+ // Subscription Manager
+
+ public createSubscription(
+ functionsRouterAddress: string,
+ consumerAddress?: string,
+ overrides?: Overrides
+ ): Promise<{ subscriptionId: BigNumber }> {
+ return functions.createSubscription(
+ this.hre,
+ functionsRouterAddress,
+ consumerAddress,
+ overrides
+ );
+ }
+
+ public fundSubscription(
+ functionsRouterAddress: string,
+ linkTokenAddress: string,
+ amountInJuels: BigNumberish,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.fundSubscription(
+ this.hre,
+ functionsRouterAddress,
+ linkTokenAddress,
+ amountInJuels,
+ subscriptionId,
+ overrides
+ );
+ }
+
+ public cancelSubscription(
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ receivingAddress?: string,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.cancelSubscription(
+ this.hre,
+ functionsRouterAddress,
+ subscriptionId,
+ receivingAddress,
+ overrides
+ );
+ }
+
+ public requestSubscriptionOwnerTransfer(
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ newOwnerAddress: string,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.requestSubscriptionOwnerTransfer(
+ this.hre,
+ functionsRouterAddress,
+ subscriptionId,
+ newOwnerAddress,
+ overrides
+ );
+ }
+
+ public acceptSubscriptionOwnerTransfer(
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.acceptSubscriptionOwnerTransfer(
+ this.hre,
+ functionsRouterAddress,
+ subscriptionId,
+ overrides
+ );
+ }
+
+ public getSubscriptionDetails(
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish
+ ): Promise {
+ return functions.getSubscriptionDetails(
+ this.hre,
+ functionsRouterAddress,
+ subscriptionId
+ );
+ }
+
+ public addConsumer(
+ functionsRouterAddress: string,
+ consumerAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.addConsumer(
+ this.hre,
+ functionsRouterAddress,
+ consumerAddress,
+ subscriptionId,
+ overrides
+ );
+ }
+
+ public removeConsumer(
+ functionsRouterAddress: string,
+ consumerAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.removeConsumer(
+ this.hre,
+ functionsRouterAddress,
+ consumerAddress,
+ subscriptionId,
+ overrides
+ );
+ }
+
+ public timeoutRequests(
+ functionsRouterAddress: string,
+ requestCommitments: functionsToolkit.RequestCommitment[],
+ overrides?: Overrides
+ ): Promise<{ transactionHash: string }> {
+ return functions.timeoutRequests(
+ this.hre,
+ functionsRouterAddress,
+ requestCommitments,
+ overrides
+ );
+ }
+
+ public estimateRequestCost(
+ functionsRouterAddress: string,
+ donId: string,
+ subscriptionId: BigNumberish,
+ callbackGasLimit: number,
+ gasPriceWei: BigNumberish,
+ overrides?: Overrides
+ ): Promise {
+ return functions.estimateRequestCost(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ subscriptionId,
+ callbackGasLimit,
+ gasPriceWei,
+ overrides
+ );
+ }
+
+ // Response Listener
+
+ public listenForResponse(
+ functionsRouterAddress: string,
+ requestId: string,
+ timeout?: number
+ ): Promise {
+ return functions.listenForResponse(
+ this.hre,
+ functionsRouterAddress,
+ requestId,
+ timeout
+ );
+ }
+
+ public listenForResponseFromTransaction(
+ functionsRouterAddress: string,
+ transactionHash: string,
+ timeout?: number,
+ confirmations?: number,
+ checkInterval?: number
+ ): Promise {
+ return functions.listenForResponseFromTransaction(
+ this.hre,
+ functionsRouterAddress,
+ transactionHash,
+ timeout,
+ confirmations,
+ checkInterval
+ );
+ }
+
+ public listenForResponses(
+ functionsRouterAddress: string,
+ subscriptionId: string,
+ callback: (functionsResponse: functionsToolkit.FunctionsResponse) => any
+ ): Promise {
+ return functions.listenForResponses(
+ this.hre,
+ functionsRouterAddress,
+ subscriptionId,
+ callback
+ );
+ }
+
+ public stopListeningForResponses(
+ functionsRouterAddress: string
+ ): Promise {
+ return functions.stopListeningForResponses(
+ this.hre,
+ functionsRouterAddress
+ );
+ }
+
+ // Secrets Manager
+
+ public fetchKeys(
+ functionsRouterAddress: string,
+ donId: string
+ ): Promise<{
+ thresholdPublicKey: functionsToolkit.ThresholdPublicKey;
+ donPublicKey: string;
+ }> {
+ return functions.fetchKeys(this.hre, functionsRouterAddress, donId);
+ }
+
+ public encryptSecretsUrls(
+ functionsRouterAddress: string,
+ donId: string,
+ secretsUrls: string[]
+ ): Promise {
+ return functions.encryptSecretsUrls(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ secretsUrls
+ );
+ }
+
+ public verifyOffchainSecrets(
+ functionsRouterAddress: string,
+ donId: string,
+ secretsUrls: string[]
+ ): Promise {
+ return functions.verifyOffchainSecrets(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ secretsUrls
+ );
+ }
+
+ public encryptSecrets(
+ functionsRouterAddress: string,
+ donId: string,
+ secrets?: Record
+ ): Promise<{
+ encryptedSecrets: string;
+ }> {
+ return functions.encryptSecrets(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ secrets
+ );
+ }
+
+ public uploadEncryptedSecretsToDON(
+ functionsRouterAddress: string,
+ donId: string,
+ encryptedSecretsHexstring: string,
+ gatewayUrls: string[],
+ slotId: number,
+ minutesUntilExpiration: number
+ ): Promise<{
+ version: number;
+ success: boolean;
+ }> {
+ return functions.uploadEncryptedSecretsToDON(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ encryptedSecretsHexstring,
+ gatewayUrls,
+ slotId,
+ minutesUntilExpiration
+ );
+ }
+
+ public listDONHostedEncryptedSecrets(
+ functionsRouterAddress: string,
+ donId: string,
+ gatewayUrls: string[]
+ ): Promise<{
+ result: functionsToolkit.GatewayResponse;
+ error?: string;
+ }> {
+ return functions.listDONHostedEncryptedSecrets(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ gatewayUrls
+ );
+ }
+
+ public buildDONHostedEncryptedSecretsReference(
+ functionsRouterAddress: string,
+ donId: string,
+ slotId: number,
+ version: number
+ ): Promise {
+ return functions.buildDONHostedEncryptedSecretsReference(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ slotId,
+ version
+ );
+ }
+
+ // utils
+ public fetchRequestCommitment(
+ functionsRouterAddress: string,
+ donId: string,
+ requestId: string,
+ toBlock?: number | "latest",
+ pastBlocksToSearch?: number,
+ overrides?: Overrides
+ ): Promise {
+ return functions.fetchRequestCommitment(
+ this.hre,
+ functionsRouterAddress,
+ donId,
+ requestId,
+ toBlock,
+ pastBlocksToSearch,
+ overrides
+ );
+ }
}
class Utils {
@@ -1045,6 +1415,47 @@ class Utils {
}> {
return utils.transferETH(this.hre, recipient, amount);
}
+
+ public async createGist(
+ githubApiToken: string,
+ content: string
+ ): Promise {
+ return utils.createGist(githubApiToken, content);
+ }
+
+ public async deleteGist(
+ githubApiToken: string,
+ gistURL: string
+ ): Promise {
+ return utils.deleteGist(githubApiToken, gistURL);
+ }
+
+ public async buildFunctionsRequestCBOR(
+ codeLocation: functionsToolkit.Location,
+ codeLanguage: functionsToolkit.CodeLanguage,
+ source: string,
+ secretsLocation?: functionsToolkit.Location,
+ encryptedSecretsReference?: string,
+ args?: string[],
+ bytesArgs?: string[]
+ ): Promise {
+ return utils.buildRequestCBOR({
+ codeLocation,
+ codeLanguage,
+ source,
+ secretsLocation,
+ encryptedSecretsReference,
+ args,
+ bytesArgs,
+ });
+ }
+
+ public async decodeHexString(
+ resultHexstring: string,
+ expectedReturnType: functionsToolkit.ReturnType
+ ): Promise {
+ return utils.decodeHexString(resultHexstring, expectedReturnType);
+ }
}
// SANDBOX
@@ -1076,6 +1487,10 @@ class Node {
return node.getP2PKeys(this.hre);
}
+ public async getVRFKeys(): Promise {
+ return node.getVRFKeys(this.hre);
+ }
+
public async getJobs(): Promise {
return node.getJobs(this.hre);
}
@@ -1104,7 +1519,7 @@ class Operator {
}
}
-class DRConsumer {
+class DirectRequestConsumer {
private hre: HardhatRuntimeEnvironment;
constructor(hre: HardhatRuntimeEnvironment) {
@@ -1112,7 +1527,7 @@ class DRConsumer {
}
public async deploy(linkTokenAddress: string): Promise {
- return drConsumer.deploy(this.hre, linkTokenAddress);
+ return directRequestConsumer.deploy(this.hre, linkTokenAddress);
}
public async requestData(
@@ -1123,7 +1538,7 @@ class DRConsumer {
pathToData: string,
multiplyTimes: string
): Promise<{ transactionHash: string }> {
- return drConsumer.requestData(
+ return directRequestConsumer.requestData(
this.hre,
directRequestConsumerAddress,
operatorAddress,
@@ -1137,7 +1552,7 @@ class DRConsumer {
public async getLatestAnswer(
directRequestConsumerAddress: string
): Promise {
- return drConsumer.getLatestAnswer(this.hre, directRequestConsumerAddress);
+ return directRequestConsumer.getLatestAnswer(this.hre, directRequestConsumerAddress);
}
}
@@ -1155,13 +1570,13 @@ class LinkToken {
public async transfer(
linkTokenAddress: string,
recipient: string,
- addedValue: BigNumberish
+ amount: BigNumberish
): Promise<{ transactionHash: string }> {
return linkToken.transfer(
this.hre,
linkTokenAddress,
recipient,
- addedValue
+ amount
);
}
@@ -1199,3 +1614,25 @@ class LinkToken {
);
}
}
+
+class FunctionsSimulation {
+ private hre: HardhatRuntimeEnvironment;
+
+ constructor(hre: HardhatRuntimeEnvironment) {
+ this.hre = hre;
+ }
+
+ public async simulateRequest(
+ source: string,
+ secrets: Record | {},
+ args: string[] | [],
+ bytesArgs: string[] | []
+ ): Promise {
+ return functionsSimulations.simulateRequest(
+ source,
+ secrets,
+ args,
+ bytesArgs
+ );
+ }
+}
diff --git a/src/automation/keepersRegistrar.ts b/src/automation/keepersRegistrar.ts
index 3684b98..7169c4b 100644
--- a/src/automation/keepersRegistrar.ts
+++ b/src/automation/keepersRegistrar.ts
@@ -23,11 +23,10 @@ const connectKeeperRegistrar = async (
keeperRegistrarAddress: string
) => {
const [signer] = await hre.ethers.getSigners();
- const typeAndVersionInterface =
- await TypeAndVersionInterface__factory.connect(
- keeperRegistrarAddress,
- signer
- );
+ const typeAndVersionInterface = TypeAndVersionInterface__factory.connect(
+ keeperRegistrarAddress,
+ signer
+ );
const typeAndVersion = await typeAndVersionInterface.typeAndVersion();
switch (typeAndVersion) {
case KeeperRegistrarVersion.registrar1_1:
@@ -219,11 +218,10 @@ export const registerUpkeep = async (
}> => {
const [signer] = await hre.ethers.getSigners();
- const typeAndVersionInterface =
- await TypeAndVersionInterface__factory.connect(
- keeperRegistrarAddress,
- signer
- );
+ const typeAndVersionInterface = TypeAndVersionInterface__factory.connect(
+ keeperRegistrarAddress,
+ signer
+ );
const typeAndVersion = await typeAndVersionInterface.typeAndVersion();
switch (typeAndVersion) {
case KeeperRegistrarVersion.registrar1_1:
@@ -326,10 +324,9 @@ export const getTypeAndVersion = async (
keeperRegistrarAddress: string
): Promise => {
const [signer] = await hre.ethers.getSigners();
- const typeAndVersionInterface =
- await TypeAndVersionInterface__factory.connect(
- keeperRegistrarAddress,
- signer
- );
+ const typeAndVersionInterface = TypeAndVersionInterface__factory.connect(
+ keeperRegistrarAddress,
+ signer
+ );
return typeAndVersionInterface.typeAndVersion();
};
diff --git a/src/automation/keepersRegistry.ts b/src/automation/keepersRegistry.ts
index 78637e9..a603156 100644
--- a/src/automation/keepersRegistry.ts
+++ b/src/automation/keepersRegistry.ts
@@ -14,11 +14,10 @@ const connectKeeperRegistry = async (
keeperRegistryAddress: string
) => {
const [signer] = await hre.ethers.getSigners();
- const typeAndVersionInterface =
- await TypeAndVersionInterface__factory.connect(
- keeperRegistryAddress,
- signer
- );
+ const typeAndVersionInterface = TypeAndVersionInterface__factory.connect(
+ keeperRegistryAddress,
+ signer
+ );
const typeAndVersion = await typeAndVersionInterface.typeAndVersion();
switch (typeAndVersion) {
case KeeperRegistryVersion.registry1_2:
@@ -137,7 +136,7 @@ export const getUpkeep = async (
executeGas: number;
checkData: BytesLike;
balance: BigNumber;
- lastAutomationNode: string | undefined;
+ lastKeeper: string | undefined;
admin: string;
maxValidBlocknumber: BigNumber;
amountSpent: BigNumber;
@@ -154,7 +153,7 @@ export const getUpkeep = async (
executeGas: upkeep.executeGas,
checkData: upkeep.checkData,
balance: upkeep.balance,
- lastAutomationNode: "lastKeeper" in upkeep ? upkeep.lastKeeper : undefined,
+ lastKeeper: "lastKeeper" in upkeep ? upkeep.lastKeeper : undefined,
admin: upkeep.admin,
maxValidBlocknumber: upkeep.maxValidBlocknumber,
amountSpent: upkeep.amountSpent,
@@ -281,11 +280,10 @@ export const getTypeAndVersion = async (
keeperRegistryAddress: string
): Promise => {
const [signer] = await hre.ethers.getSigners();
- const typeAndVersionInterface =
- await TypeAndVersionInterface__factory.connect(
- keeperRegistryAddress,
- signer
- );
+ const typeAndVersionInterface = TypeAndVersionInterface__factory.connect(
+ keeperRegistryAddress,
+ signer
+ );
return typeAndVersionInterface.typeAndVersion();
};
diff --git a/src/functions/index.ts b/src/functions/index.ts
index e69de29..022c118 100644
--- a/src/functions/index.ts
+++ b/src/functions/index.ts
@@ -0,0 +1,704 @@
+import * as functionsToolkit from "@chainlink/functions-toolkit";
+import {
+ FunctionsResponse,
+ GatewayResponse,
+ RequestCommitment,
+ ThresholdPublicKey,
+} from "@chainlink/functions-toolkit/dist/types";
+import { BigNumber, BigNumberish, providers, Signer } from "ethers";
+import { HardhatRuntimeEnvironment } from "hardhat/types";
+
+import { FunctionsSubscriptionDetails, Overrides } from "../shared/types";
+
+// DIRECT METHOD CALLS
+export const createSubscription = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ consumerAddress?: string,
+ overrides?: Overrides
+): Promise<{ subscriptionId: BigNumber }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.createSubscription(consumerAddress);
+};
+
+export const fundSubscription = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ linkTokenAddress: string,
+ amountInJuels: BigNumberish,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ linkTokenAddress,
+ overrides,
+ });
+ return functionsRouter.fundSubscription(amountInJuels, subscriptionId);
+};
+
+export const cancelSubscription = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ receivingAddress?: string,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.cancelSubscription(subscriptionId, receivingAddress);
+};
+
+export const requestSubscriptionOwnerTransfer = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ newOwnerAddress: string,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.requestSubscriptionOwnerTransfer(
+ subscriptionId,
+ newOwnerAddress
+ );
+};
+
+export const acceptSubscriptionOwnerTransfer = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.acceptSubscriptionOwnerTransfer(subscriptionId);
+};
+
+export const getSubscriptionDetails = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ subscriptionId: BigNumberish
+): Promise => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ });
+ return functionsRouter.getSubscriptionDetails(subscriptionId);
+};
+
+export const addConsumer = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ consumerAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.addConsumer(subscriptionId, consumerAddress);
+};
+
+export const removeConsumer = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ consumerAddress: string,
+ subscriptionId: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.removeConsumer(subscriptionId, consumerAddress);
+};
+
+export const timeoutRequests = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ requestCommitments: RequestCommitment[],
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.timeoutRequests(requestCommitments);
+};
+
+export const estimateRequestCost = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ subscriptionId: BigNumberish,
+ callbackGasLimit: number,
+ gasPriceWei: BigNumberish,
+ overrides?: Overrides
+): Promise => {
+ const functionsRouter = await FunctionsSubscriptionManager.initialize({
+ hre,
+ functionsRouterAddress,
+ overrides,
+ });
+ return functionsRouter.estimateRequestCost(
+ donId,
+ subscriptionId,
+ callbackGasLimit,
+ gasPriceWei
+ );
+};
+
+export const listenForResponse = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ requestId: string,
+ timeout?: number
+): Promise => {
+ const functionsResponseListener = await FunctionsResponseListener.initialize({
+ hre,
+ functionsRouterAddress,
+ });
+ return functionsResponseListener.listenForResponse(requestId, timeout);
+};
+
+export const listenForResponseFromTransaction = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ transactionHash: string,
+ timeout?: number,
+ confirmations?: number,
+ checkInterval?: number
+): Promise => {
+ const functionsResponseListener = await FunctionsResponseListener.initialize({
+ hre,
+ functionsRouterAddress,
+ });
+ return functionsResponseListener.listenForResponseFromTransaction(
+ transactionHash,
+ timeout,
+ confirmations,
+ checkInterval
+ );
+};
+
+export const listenForResponses = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ subscriptionId: string,
+ callback: (functionsResponse: FunctionsResponse) => any
+): Promise => {
+ const functionsResponseListener = await FunctionsResponseListener.initialize({
+ hre,
+ functionsRouterAddress,
+ });
+ return functionsResponseListener.listenForResponses(subscriptionId, callback);
+};
+
+export const stopListeningForResponses = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string
+): Promise => {
+ const functionsResponseListener = await FunctionsResponseListener.initialize({
+ hre,
+ functionsRouterAddress,
+ });
+ return functionsResponseListener.stopListeningForResponses();
+};
+
+export const fetchKeys = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string
+): Promise<{
+ thresholdPublicKey: ThresholdPublicKey;
+ donPublicKey: string;
+}> => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.fetchKeys();
+};
+
+export const encryptSecretsUrls = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ secretsUrls: string[]
+): Promise => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.encryptSecretsUrls(secretsUrls);
+};
+
+export const verifyOffchainSecrets = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ secretsUrls: string[]
+): Promise => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.verifyOffchainSecrets(secretsUrls);
+};
+
+export const encryptSecrets = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ secrets?: Record
+): Promise<{
+ encryptedSecrets: string;
+}> => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.encryptSecrets(secrets);
+};
+
+export const uploadEncryptedSecretsToDON = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ encryptedSecretsHexstring: string,
+ gatewayUrls: string[],
+ slotId: number,
+ minutesUntilExpiration: number
+): Promise<{
+ version: number;
+ success: boolean;
+}> => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.uploadEncryptedSecretsToDON(
+ encryptedSecretsHexstring,
+ gatewayUrls,
+ slotId,
+ minutesUntilExpiration
+ );
+};
+
+export const listDONHostedEncryptedSecrets = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ gatewayUrls: string[]
+): Promise<{
+ result: GatewayResponse;
+ error?: string;
+}> => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.listDONHostedEncryptedSecrets(gatewayUrls);
+};
+
+export const buildDONHostedEncryptedSecretsReference = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ slotId: number,
+ version: number
+): Promise => {
+ const functionsSecretsManager = await FunctionsSecretsManager.initialize({
+ hre,
+ functionsRouterAddress,
+ donId,
+ });
+ return functionsSecretsManager.buildDONHostedEncryptedSecretsReference(
+ slotId,
+ version
+ );
+};
+
+export const fetchRequestCommitment = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ requestId: string,
+ toBlock?: number | "latest",
+ pastBlocksToSearch?: number,
+ overrides?: Overrides
+): Promise => {
+ const provider = overrides?.provider || hre.ethers.provider;
+ return functionsToolkit.fetchRequestCommitment({
+ requestId,
+ provider,
+ functionsRouterAddress,
+ donId,
+ toBlock,
+ pastBlocksToSearch,
+ });
+};
+
+export class FunctionsSubscriptionManager {
+ private hre: HardhatRuntimeEnvironment;
+ private subscriptionManager: functionsToolkit.SubscriptionManager;
+
+ private constructor(
+ hre: HardhatRuntimeEnvironment,
+ signer: Signer,
+ functionsRouterAddress: string,
+ linkTokenAddress?: string
+ ) {
+ this.hre = hre;
+ this.subscriptionManager = new functionsToolkit.SubscriptionManager({
+ signer,
+ functionsRouterAddress,
+ linkTokenAddress: linkTokenAddress || "",
+ });
+ }
+
+ // static async class factory
+ static async initialize(args: {
+ hre: HardhatRuntimeEnvironment;
+ functionsRouterAddress: string;
+ linkTokenAddress?: string;
+ overrides?: Overrides;
+ }): Promise {
+ const { hre, functionsRouterAddress, linkTokenAddress, overrides } = args;
+ const accounts = await hre.ethers.getSigners();
+ const functionsSubscriptionManager = new FunctionsSubscriptionManager(
+ hre,
+ overrides?.signer || accounts[0],
+ functionsRouterAddress,
+ linkTokenAddress
+ );
+ await functionsSubscriptionManager.subscriptionManager.initialize();
+ return functionsSubscriptionManager;
+ }
+
+ async createSubscription(
+ consumerAddress?: string
+ ): Promise<{ subscriptionId: BigNumber }> {
+ const subscriptionId = await this.subscriptionManager.createSubscription({
+ consumerAddress,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ subscriptionId: BigNumber.from(subscriptionId),
+ };
+ }
+
+ async fundSubscription(
+ amountInJuels: BigNumberish,
+ subscriptionId: BigNumberish
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.fundSubscription({
+ juelsAmount: amountInJuels.toString(),
+ subscriptionId: subscriptionId.toString(),
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ async getSubscriptionDetails(
+ subscriptionId: BigNumberish
+ ): Promise {
+ const result = await this.subscriptionManager.getSubscriptionInfo(
+ subscriptionId.toString()
+ );
+ return {
+ balance: BigNumber.from(result.balance),
+ owner: result.owner,
+ blockedBalance: BigNumber.from(result.blockedBalance),
+ proposedOwner: result.proposedOwner,
+ consumers: result.consumers,
+ flags: result.flags,
+ };
+ }
+
+ async cancelSubscription(
+ subscriptionId: BigNumberish,
+ receivingAddress?: string
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.cancelSubscription({
+ subscriptionId: subscriptionId.toString(),
+ refundAddress: receivingAddress,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ async requestSubscriptionOwnerTransfer(
+ subscriptionId: BigNumberish,
+ newOwnerAddress: string
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.requestSubscriptionTransfer({
+ subscriptionId: subscriptionId.toString(),
+ newOwner: newOwnerAddress,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ async acceptSubscriptionOwnerTransfer(
+ subscriptionId: BigNumberish
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.acceptSubTransfer({
+ subscriptionId: subscriptionId.toString(),
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ async addConsumer(
+ subscriptionId: BigNumberish,
+ consumerAddress: string
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.addConsumer({
+ subscriptionId: subscriptionId.toString(),
+ consumerAddress,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ async removeConsumer(
+ subscriptionId: BigNumberish,
+ consumerAddress: string
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.removeConsumer({
+ subscriptionId: subscriptionId.toString(),
+ consumerAddress,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+ async timeoutRequests(
+ requestCommitments: RequestCommitment[]
+ ): Promise<{ transactionHash: string }> {
+ const tx = await this.subscriptionManager.timeoutRequests({
+ requestCommitments,
+ txOptions: {
+ confirmations: this.hre.config.chainlink.confirmations,
+ },
+ });
+ return {
+ transactionHash: tx.transactionHash,
+ };
+ }
+
+ estimateRequestCost(
+ donId: string,
+ subscriptionId: BigNumberish,
+ callbackGasLimit: number,
+ gasPriceWei: BigNumberish
+ ): Promise {
+ return this.subscriptionManager.estimateFunctionsRequestCost({
+ donId,
+ subscriptionId: subscriptionId.toString(),
+ callbackGasLimit,
+ gasPriceWei: BigInt(gasPriceWei.toString()),
+ });
+ }
+}
+
+export class FunctionsResponseListener {
+ private responseListener: functionsToolkit.ResponseListener;
+
+ private constructor(
+ functionsRouterAddress: string,
+ provider: providers.JsonRpcProvider
+ ) {
+ this.responseListener = new functionsToolkit.ResponseListener({
+ provider,
+ functionsRouterAddress,
+ });
+ }
+
+ // static async class factory
+ static async initialize(args: {
+ hre: HardhatRuntimeEnvironment;
+ functionsRouterAddress: string;
+ overrides?: Overrides;
+ }): Promise {
+ const { hre, functionsRouterAddress, overrides } = args;
+ const provider = overrides?.provider || hre.ethers.provider;
+ return new FunctionsResponseListener(functionsRouterAddress, provider);
+ }
+
+ listenForResponse(
+ requestId: string,
+ timeout?: number
+ ): Promise {
+ return this.responseListener.listenForResponse(requestId, timeout);
+ }
+
+ listenForResponseFromTransaction(
+ transactionHash: string,
+ timeout?: number,
+ confirmations?: number,
+ checkInterval?: number
+ ): Promise {
+ return this.responseListener.listenForResponseFromTransaction(
+ transactionHash,
+ timeout,
+ confirmations,
+ checkInterval
+ );
+ }
+
+ listenForResponses(
+ subscriptionId: number | string,
+ callback: (functionsResponse: FunctionsResponse) => any
+ ): void {
+ return this.responseListener.listenForResponses(subscriptionId, callback);
+ }
+
+ stopListeningForResponses(): void {
+ return this.responseListener.stopListeningForResponses();
+ }
+}
+
+export class FunctionsSecretsManager {
+ private secretsManager: functionsToolkit.SecretsManager;
+
+ private constructor(
+ signer: Signer,
+ functionsRouterAddress: string,
+ donId: string
+ ) {
+ this.secretsManager = new functionsToolkit.SecretsManager({
+ signer,
+ functionsRouterAddress,
+ donId,
+ });
+ }
+
+ // static async class factory
+ static async initialize(args: {
+ hre: HardhatRuntimeEnvironment;
+ functionsRouterAddress: string;
+ donId: string;
+ overrides?: Overrides;
+ }): Promise {
+ const { hre, functionsRouterAddress, donId, overrides } = args;
+ const account = await hre.ethers.getSigners();
+ const signer = overrides?.signer || account[0];
+ const functionsSecretsManager = new FunctionsSecretsManager(
+ signer,
+ functionsRouterAddress,
+ donId
+ );
+ await functionsSecretsManager.secretsManager.initialize();
+
+ return functionsSecretsManager;
+ }
+
+ fetchKeys(): Promise<{
+ thresholdPublicKey: ThresholdPublicKey;
+ donPublicKey: string;
+ }> {
+ return this.secretsManager.fetchKeys();
+ }
+
+ encryptSecretsUrls(secretsUrls: string[]): Promise {
+ return this.secretsManager.encryptSecretsUrls(secretsUrls);
+ }
+
+ verifyOffchainSecrets(secretsUrls: string[]): Promise {
+ return this.secretsManager.verifyOffchainSecrets(secretsUrls);
+ }
+
+ encryptSecrets(secrets?: Record): Promise<{
+ encryptedSecrets: string;
+ }> {
+ return this.secretsManager.encryptSecrets(secrets);
+ }
+
+ uploadEncryptedSecretsToDON(
+ encryptedSecretsHexstring: string,
+ gatewayUrls: string[],
+ slotId: number,
+ minutesUntilExpiration: number
+ ): Promise<{
+ version: number;
+ success: boolean;
+ }> {
+ return this.secretsManager.uploadEncryptedSecretsToDON({
+ encryptedSecretsHexstring,
+ gatewayUrls,
+ slotId,
+ minutesUntilExpiration,
+ });
+ }
+
+ listDONHostedEncryptedSecrets(gatewayUrls: string[]): Promise<{
+ result: GatewayResponse;
+ error?: string;
+ }> {
+ return this.secretsManager.listDONHostedEncryptedSecrets(gatewayUrls);
+ }
+
+ buildDONHostedEncryptedSecretsReference(
+ slotId: number,
+ version: number
+ ): string {
+ return this.secretsManager.buildDONHostedEncryptedSecretsReference({
+ slotId,
+ version,
+ });
+ }
+}
diff --git a/src/helpers/inquirers.ts b/src/helpers/inquirers.ts
index 94b4520..1a4f27c 100644
--- a/src/helpers/inquirers.ts
+++ b/src/helpers/inquirers.ts
@@ -1,3 +1,7 @@
+import {
+ CodeLanguage,
+ Location,
+} from "@chainlink/functions-toolkit/dist/types";
import confirm from "@inquirer/confirm";
import input from "@inquirer/input";
import select from "@inquirer/select";
@@ -10,7 +14,7 @@ import {
DataFeedsRegistry,
DenominationsRegistry,
FeedRegistriesRegistry,
- FunctionOraclesRegistry,
+ FunctionsRoutersRegistry,
KeeperRegistriesRegistry,
L2SequencersRegistry,
LinkTokensRegistry,
@@ -44,8 +48,15 @@ export const inquire = async (
return inquireKeeperRegistrarAddress(hre);
case InquirableParameter.l2SequencerAddress:
return inquireL2SequencerAddress(hre);
- case InquirableParameter.functionOracleAddress:
- return inquireFunctionOracleAddress(hre);
+ case InquirableParameter.functionsRouterAddress:
+ return inquireFunctionsRouterAddress(hre);
+ case InquirableParameter.donId:
+ return inquireDonId(hre);
+ case InquirableParameter.codeLocation:
+ case InquirableParameter.secretsLocation:
+ return inquireLocation();
+ case InquirableParameter.codeLanguage:
+ return inquireCodeLanguage();
case InquirableParameter.feedRegistryBaseTick:
return inquireFeedRegistryBaseTick();
case InquirableParameter.feedRegistryQuoteTick:
@@ -610,15 +621,15 @@ export const inquireL2SequencerAddress = async (
return l2SequencerAddress;
};
-export const inquireFunctionOracle = async (
+export const inquireFunctionsRouter = async (
hre: HardhatRuntimeEnvironment,
useHardhatNetwork: boolean = true
) => {
const networksRegistry: NetworksRegistry =
registries.networksRegistry as NetworksRegistry;
- const functionOraclesRegistry: FunctionOraclesRegistry =
- registries.functionOraclesRegistry as FunctionOraclesRegistry;
+ const functionsRoutersRegistry: FunctionsRoutersRegistry =
+ registries.functionsRoutersRegistry as FunctionsRoutersRegistry;
let chainSlug = "";
if (useHardhatNetwork) {
@@ -634,7 +645,7 @@ export const inquireFunctionOracle = async (
} else {
chainSlug = await select({
message: "Select a network",
- choices: Object.values(Object.keys(functionOraclesRegistry)).reduce(
+ choices: Object.values(Object.keys(functionsRoutersRegistry)).reduce(
(agg, networkName) => {
agg.push({
name: networksRegistry[networkName].name,
@@ -648,40 +659,66 @@ export const inquireFunctionOracle = async (
});
}
- if (!functionOraclesRegistry[chainSlug]) {
+ if (!functionsRoutersRegistry[chainSlug]) {
console.log(
- `There is no Function Oracle in the plugin registry for the selected chain: ${hre.network.name}`
+ `There is no Function Router in the plugin registry for the selected chain: ${hre.network.name}`
);
return undefined;
}
- return functionOraclesRegistry[chainSlug];
+ return functionsRoutersRegistry[chainSlug];
};
-export const inquireFunctionOracleAddress = async (
+export const inquireFunctionsRouterAddress = async (
hre: HardhatRuntimeEnvironment,
useHardhatNetwork: boolean = true
) => {
- const functionOracle = await inquireFunctionOracle(hre, useHardhatNetwork);
- if (!functionOracle) {
+ const functionsRouter = await inquireFunctionsRouter(hre, useHardhatNetwork);
+ if (!functionsRouter) {
return input({
- message: "Provide a valid Function Oracle address",
+ message: "Provide a valid Functions Router address",
});
}
- const functionOracleAddress = functionOracle.contractAddress;
+ const functionsRouterAddress = functionsRouter.contractAddress;
const answer: boolean = await confirm({
- message: `Function Oracle found in the plugin registry: ${functionOracleAddress}. Do you want to proceed with it?`,
+ message: `Functions Router found in the plugin registry: ${functionsRouterAddress}. Do you want to proceed with it?`,
});
if (!answer) {
return input({
- message: "Provide a valid Function Oracle address",
+ message: "Provide a valid Functions Router address",
});
}
- return functionOracleAddress;
+ return functionsRouterAddress;
+};
+
+export const inquireDonId = async (
+ hre: HardhatRuntimeEnvironment,
+ useHardhatNetwork: boolean = true
+) => {
+ const functionsRouter = await inquireFunctionsRouter(hre, useHardhatNetwork);
+ if (!functionsRouter) {
+ return input({
+ message: "Provide a valid Functions Router address",
+ });
+ }
+
+ const donId = functionsRouter.donId;
+
+ const answer: boolean = await confirm({
+ message: `DON ID found in the plugin registry: ${donId}. Do you want to proceed with it?`,
+ });
+
+ if (!answer) {
+ return input({
+ message: "Provide a valid DON ID",
+ });
+ }
+
+ return donId;
};
export const inquireFeedRegistryBaseTick = async () => {
@@ -731,6 +768,38 @@ export const inquireDenomination = async () => {
return denomination;
};
+export const inquireLocation = async () => {
+ const keys = Object.keys(Location);
+ const values = Object.values(Location);
+ return select({
+ message: "Select a location",
+ choices: keys.reduce((agg, _, currentIndex) => {
+ agg.push({
+ name: keys[currentIndex],
+ value: values[currentIndex].toString(),
+ description: keys[currentIndex],
+ });
+ return agg;
+ }, [] as Choice[]),
+ });
+};
+
+export const inquireCodeLanguage = async () => {
+ const keys = Object.keys(CodeLanguage);
+ const values = Object.values(CodeLanguage);
+ return select({
+ message: "Select a code language",
+ choices: keys.reduce((agg, _, currentIndex) => {
+ agg.push({
+ name: keys[currentIndex],
+ value: values[currentIndex].toString(),
+ description: keys[currentIndex],
+ });
+ return agg;
+ }, [] as Choice[]),
+ });
+};
+
export const inquireSubtaskProperties = async (
task: Task
): Promise<[string, SubtaskProperties]> => {
diff --git a/src/index.ts b/src/index.ts
index 33d22dd..69a0d5d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,17 +15,17 @@ import "./type-extensions";
export interface ChainlinkUserConfig {
confirmations?: number;
- node: {
- chain_id: string;
- chain_name: string;
- http_url: string;
- ws_url: string;
- cl_keystore_password: string;
- cl_api_user: string;
- cl_api_password: string;
- pg_user: string;
- pg_password: string;
- pg_db: string;
+ node?: {
+ chain_id?: string;
+ chain_name?: string;
+ http_url?: string;
+ ws_url?: string;
+ cl_keystore_password?: string;
+ cl_api_user?: string;
+ cl_api_password?: string;
+ pg_user?: string;
+ pg_password?: string;
+ pg_db?: string;
};
}
@@ -46,17 +46,16 @@ extendConfig(
config.chainlink = {
confirmations: confirmations || 1,
node: {
- chain_id: node?.chain_id || "1337",
- chain_name: node?.chain_name || "local",
- http_url: node?.http_url || "http://host.docker.internal:8545",
- ws_url: node?.ws_url || "ws://host.docker.internal:8545",
- cl_keystore_password:
- node?.cl_keystore_password || "password1234567890",
- cl_api_user: node?.cl_api_user || "user@chain.link",
- cl_api_password: node?.cl_api_password || "password1234567890",
- pg_user: node?.pg_user || "chainlink",
- pg_password: node?.pg_password || "password1234567890",
- pg_db: node?.pg_db || "chainlink",
+ chain_id: node?.chain_id,
+ chain_name: node?.chain_name,
+ http_url: node?.http_url,
+ ws_url: node?.ws_url,
+ cl_keystore_password: node?.cl_keystore_password,
+ cl_api_user: node?.cl_api_user,
+ cl_api_password: node?.cl_api_password,
+ pg_user: node?.pg_user,
+ pg_password: node?.pg_password,
+ pg_db: node?.pg_db,
},
};
}
@@ -189,6 +188,21 @@ task(
printSubtasks(Task.automationRegistry);
});
+// FUNCTIONS
+task(`${PACKAGE_NAME}:${Task.functions}`, "Functions Module")
+ .addOptionalPositionalParam("subtask", "Subtask")
+ .addOptionalParam("args", "Subtask args")
+ .setAction(async (taskArgs, hre) => {
+ return resolveTask(hre, Task.functions, taskArgs);
+ });
+
+task(
+ `${PACKAGE_NAME}:${Task.functions}:subtasks`,
+ "Functions Module: Subtasks List"
+).setAction(async () => {
+ printSubtasks(Task.functions);
+});
+
// REGISTRIES
task(`${PACKAGE_NAME}:${Task.registries}`, "Plugin Registries Module")
.addOptionalPositionalParam("subtask", "Subtask")
@@ -235,18 +249,18 @@ task(
});
// DIRECT REQUEST CONSUMER
-task(`${PACKAGE_NAME}:${Task.drConsumer}`, "Direct Request Consumer Module")
+task(`${PACKAGE_NAME}:${Task.directRequestConsumer}`, "Direct Request Consumer Module")
.addOptionalPositionalParam("subtask", "Subtask")
.addOptionalParam("args", "Subtask args")
.setAction(async (taskArgs, hre) => {
- return resolveTask(hre, Task.drConsumer, taskArgs);
+ return resolveTask(hre, Task.directRequestConsumer, taskArgs);
});
task(
- `${PACKAGE_NAME}:${Task.drConsumer}:subtasks`,
+ `${PACKAGE_NAME}:${Task.directRequestConsumer}:subtasks`,
"Direct Request Consumer Module: Subtasks List"
).setAction(async () => {
- printSubtasks(Task.drConsumer);
+ printSubtasks(Task.directRequestConsumer);
});
// LINK TOKEN
@@ -264,6 +278,24 @@ task(
printSubtasks(Task.linkToken);
});
+// FUNCTIONS SIMULATION
+task(
+ `${PACKAGE_NAME}:${Task.functionsSimulation}`,
+ "Functions Simulation Module"
+)
+ .addOptionalPositionalParam("subtask", "Subtask")
+ .addOptionalParam("args", "Subtask args")
+ .setAction(async (taskArgs, hre) => {
+ return resolveTask(hre, Task.functionsSimulation, taskArgs);
+ });
+
+task(
+ `${PACKAGE_NAME}:${Task.functionsSimulation}:subtasks`,
+ "Functions Simulation Module: Subtasks List"
+).setAction(async () => {
+ printSubtasks(Task.functionsSimulation);
+});
+
// UTILS
task(`${PACKAGE_NAME}:${Task.utils}`, "Plugin Utils Module")
.addOptionalPositionalParam("subtask", "Subtask")
diff --git a/src/registries/functionOraclesRegistry.ts b/src/registries/functionOraclesRegistry.ts
deleted file mode 100644
index 62a156b..0000000
--- a/src/registries/functionOraclesRegistry.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export const functionOraclesRegistry = {
- sepolia: {
- contractAddress: "0x649a2C205BE7A3d5e99206CEEFF30c794f0E31EC",
- registryAddress: "0x3c79f56407DCB9dc9b852D139a317246f43750Cc",
- chainId: "11155111",
- },
- mumbai: {
- contractAddress: "0xeA6721aC65BCeD841B8ec3fc5fEdeA6141a0aDE4",
- registryAddress: "0xEe9Bf52E5Ea228404bB54BCFbbDa8c21131b9039",
- chainId: "80001",
- },
- fuji: {
- contractAddress: "0xE569061eD8244643169e81293b0aA0d3335fD563",
- registryAddress: "0x452C33Cef9Bc773267Ac5F8D85c1Aca2bA4bcf0C",
- chainId: "43113",
- },
-};
diff --git a/src/registries/functionsRoutersRegistry.ts b/src/registries/functionsRoutersRegistry.ts
new file mode 100644
index 0000000..cea82b4
--- /dev/null
+++ b/src/registries/functionsRoutersRegistry.ts
@@ -0,0 +1,68 @@
+export const functionsRoutersRegistry = {
+ ethereum: {
+ contractAddress: "0x65Dcc24F8ff9e51F10DCc7Ed1e4e2A61e6E14bd6",
+ donId: "fun-ethereum-mainnet-1",
+ donIdBytes32:
+ "0x66756e2d657468657265756d2d6d61696e6e65742d3100000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/",
+ ],
+ chainId: "1",
+ },
+ sepolia: {
+ contractAddress: "0xb83E47C2bC239B3bf370bc41e1459A34b41238D0",
+ donId: "fun-ethereum-mainnet-1",
+ donIdBytes32:
+ "0x66756e2d657468657265756d2d6d61696e6e65742d3100000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/",
+ ],
+ chainId: "11155111",
+ },
+ polygon: {
+ contractAddress: "0xdc2AAF042Aeff2E68B3e8E33F19e4B9fA7C73F10",
+ donId: "fun-polygon-mainnet-1",
+ donIdBytes32:
+ "0x66756e2d706f6c79676f6e2d6d61696e6e65742d310000000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/",
+ ],
+ chainId: "137",
+ },
+ mumbai: {
+ contractAddress: "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C",
+ donId: "fun-polygon-mumbai-1",
+ donIdBytes32:
+ "0x66756e2d706f6c79676f6e2d6d756d6261692d31000000000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.testnet.chain.link/",
+ "https://02.functions-gateway.testnet.chain.link/",
+ ],
+ chainId: "80001",
+ },
+ avalanche: {
+ contractAddress: "0x9f82a6A0758517FD0AfA463820F586999AF314a0",
+ donId: "fun-avalanche-mainnet-1",
+ donIdBytes32:
+ "0x66756e2d6176616c616e6368652d6d61696e6e65742d31000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/",
+ ],
+ chainId: "43114",
+ },
+ fuji: {
+ contractAddress: "0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0",
+ donId: "fun-avalanche-fuji-1",
+ donIdBytes32:
+ "0x66756e2d6176616c616e6368652d66756a692d31000000000000000000000000",
+ gatewayUrls: [
+ "https://01.functions-gateway.testnet.chain.link/",
+ "https://02.functions-gateway.testnet.chain.link/",
+ ],
+ chainId: "43113",
+ },
+};
diff --git a/src/registries/helpers/build.ts b/src/registries/helpers/build.ts
index de3d6a6..7f99b1e 100644
--- a/src/registries/helpers/build.ts
+++ b/src/registries/helpers/build.ts
@@ -4,7 +4,7 @@ import { kebabToCamelCase } from "../../helpers/utils";
import {
DataFeedsRegistry,
FeedRegistriesRegistry,
- FunctionOraclesRegistry,
+ FunctionsRoutersRegistry,
KeeperRegistriesRegistry,
L2SequencersRegistry,
LinkTokensRegistry,
@@ -14,7 +14,7 @@ import {
import {
DataFeed,
FeedRegistry,
- FunctionOracle,
+ FunctionsRouter,
KeeperRegistry,
L2Sequencer,
LinkToken,
@@ -23,7 +23,7 @@ import {
} from "../interfaces";
import dataFeedsJSON from "../json/DataFeeds.json";
import feedRegistriesJSON from "../json/FeedRegistries.json";
-import functionOraclesJSON from "../json/FunctionOracles.json";
+import functionsRoutersJSON from "../json/FunctionsRouters.json";
import keeperRegistriesJSON from "../json/KeeperRegistries.json";
import l2SequencersJson from "../json/L2Sequencers.json";
import linkTokensJSON from "../json/LinkTokens.json";
@@ -35,8 +35,8 @@ const feedRegistries: FeedRegistry[] = feedRegistriesJSON as FeedRegistry[];
const networks: Network[] = networksJSON as Network[];
const vrfCoordinators: VRFCoordinator[] =
vrfCoordinatorsJSON as VRFCoordinator[];
-const functionOracles: FunctionOracle[] =
- functionOraclesJSON as FunctionOracle[];
+const functionsRouters: FunctionsRouter[] =
+ functionsRoutersJSON as FunctionsRouter[];
const linkTokens: LinkToken[] = linkTokensJSON as LinkToken[];
const keeperRegistries: KeeperRegistry[] =
keeperRegistriesJSON as KeeperRegistry[];
@@ -46,7 +46,7 @@ const networksMap: NetworksRegistry = {};
const dataFeedsMap: DataFeedsRegistry = {};
const feedRegistriesMap: FeedRegistriesRegistry = {};
const vrfCoordinatorsMap: VRFCoordinatorsRegistry = {};
-const functionOraclesMap: FunctionOraclesRegistry = {};
+const functionsRoutersMap: FunctionsRoutersRegistry = {};
const linkTokensMap: LinkTokensRegistry = {};
const keeperRegistriesMap: KeeperRegistriesRegistry = {};
const l2SequencersMap: L2SequencersRegistry = {};
@@ -78,9 +78,9 @@ vrfCoordinators.forEach((vrfCoordinator: VRFCoordinator) => {
vrfCoordinatorsMap[kebabToCamelCase(chainSlug)] = vrfCoordinator;
});
-functionOracles.forEach((functionOracle: FunctionOracle) => {
- const chainSlug = networksMap[functionOracle.chainId].chainSlug;
- functionOraclesMap[kebabToCamelCase(chainSlug)] = functionOracle;
+functionsRouters.forEach((functionsRouter: FunctionsRouter) => {
+ const chainSlug = networksMap[functionsRouter.chainId].chainSlug;
+ functionsRoutersMap[kebabToCamelCase(chainSlug)] = functionsRouter;
});
linkTokens.forEach((linkToken: LinkToken) => {
@@ -110,8 +110,8 @@ const tsCodeFeedRegistries = `export const feedRegistriesRegistry = ${JSON.strin
const tsCodeVRFCoordinators = `export const vrfCoordinatorsRegistry = ${JSON.stringify(
vrfCoordinatorsMap
)};`;
-const tsCodeFunctionOracles = `export const functionOraclesRegistry = ${JSON.stringify(
- functionOraclesMap
+const tsCodeFunctionsRouters = `export const functionsRoutersRegistry = ${JSON.stringify(
+ functionsRoutersMap
)};`;
const tsCodeLinkTokens = `export const linkTokensRegistry = ${JSON.stringify(
linkTokensMap
@@ -127,7 +127,7 @@ fs.writeFileSync("../networksRegistry.ts", tsCodeNetworks);
fs.writeFileSync("../dataFeedsRegistry.ts", tsCodeDataFeeds);
fs.writeFileSync("../feedRegistriesRegistry.ts", tsCodeFeedRegistries);
fs.writeFileSync("../vrfCoordinatorsRegistry.ts", tsCodeVRFCoordinators);
-fs.writeFileSync("../functionOraclesRegistry.ts", tsCodeFunctionOracles);
+fs.writeFileSync("../functionsRoutersRegistry.ts", tsCodeFunctionsRouters);
fs.writeFileSync("../linkTokensRegistry.ts", tsCodeLinkTokens);
fs.writeFileSync("../keeperRegistriesRegistry.ts", tsCodeKeeperRegistries);
fs.writeFileSync("../l2SequencersRegistry.ts", tsCodeL2Sequencers);
diff --git a/src/registries/index.ts b/src/registries/index.ts
index fd3c5d1..e0dfcb5 100644
--- a/src/registries/index.ts
+++ b/src/registries/index.ts
@@ -2,7 +2,7 @@ export * from "./networksRegistry";
export * from "./dataFeedsRegistry";
export * from "./feedRegistriesRegistry";
export * from "./vrfCoordinatorsRegistry";
-export * from "./functionOraclesRegistry";
+export * from "./functionsRoutersRegistry";
export * from "./linkTokensRegistry";
export * from "./keeperRegistriesRegistry";
export * from "./l2SequencersRegistry";
diff --git a/src/registries/interfaces/functionOracle.interface.ts b/src/registries/interfaces/functionOracle.interface.ts
deleted file mode 100644
index db2998c..0000000
--- a/src/registries/interfaces/functionOracle.interface.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface FunctionOracle {
- contractAddress: string;
- registryAddress: string;
- chainId: string;
-}
diff --git a/src/registries/interfaces/functionRouter.interface.ts b/src/registries/interfaces/functionRouter.interface.ts
new file mode 100644
index 0000000..f10813d
--- /dev/null
+++ b/src/registries/interfaces/functionRouter.interface.ts
@@ -0,0 +1,7 @@
+export interface FunctionsRouter {
+ contractAddress: string;
+ donId: string;
+ donIdBytes32: string;
+ gatewayUrls: string[];
+ chainId: string;
+}
diff --git a/src/registries/interfaces/index.ts b/src/registries/interfaces/index.ts
index b02c61c..67ec2a7 100644
--- a/src/registries/interfaces/index.ts
+++ b/src/registries/interfaces/index.ts
@@ -1,6 +1,6 @@
export * from "./dataFeed.interface";
export * from "./feedRegistry.interface";
-export * from "./functionOracle.interface";
+export * from "./functionRouter.interface";
export * from "./keeperRegistry.interface";
export * from "./linkToken.interface";
export * from "./network.interface";
diff --git a/src/registries/json/FunctionOracles.json b/src/registries/json/FunctionOracles.json
deleted file mode 100644
index 552785d..0000000
--- a/src/registries/json/FunctionOracles.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
- {
- "contractAddress": "0x649a2C205BE7A3d5e99206CEEFF30c794f0E31EC",
- "registryAddress": "0x3c79f56407DCB9dc9b852D139a317246f43750Cc",
- "chainId": "11155111"
- },
- {
- "contractAddress": "0xeA6721aC65BCeD841B8ec3fc5fEdeA6141a0aDE4",
- "registryAddress": "0xEe9Bf52E5Ea228404bB54BCFbbDa8c21131b9039",
- "chainId": "80001"
- },
- {
- "contractAddress": "0xE569061eD8244643169e81293b0aA0d3335fD563",
- "registryAddress": "0x452C33Cef9Bc773267Ac5F8D85c1Aca2bA4bcf0C",
- "chainId": "43113"
- }
-]
diff --git a/src/registries/json/FunctionsRouters.json b/src/registries/json/FunctionsRouters.json
new file mode 100644
index 0000000..7a230db
--- /dev/null
+++ b/src/registries/json/FunctionsRouters.json
@@ -0,0 +1,62 @@
+[
+ {
+ "contractAddress": "0x65Dcc24F8ff9e51F10DCc7Ed1e4e2A61e6E14bd6",
+ "donId": "fun-ethereum-mainnet-1",
+ "donIdBytes32": "0x66756e2d657468657265756d2d6d61696e6e65742d3100000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/"
+ ],
+ "chainId": "1"
+ },
+ {
+ "contractAddress": "0xb83E47C2bC239B3bf370bc41e1459A34b41238D0",
+ "donId": "fun-ethereum-mainnet-1",
+ "donIdBytes32": "0x66756e2d657468657265756d2d6d61696e6e65742d3100000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/"
+ ],
+ "chainId": "11155111"
+ },
+ {
+ "contractAddress": "0xdc2AAF042Aeff2E68B3e8E33F19e4B9fA7C73F10",
+ "donId": "fun-polygon-mainnet-1",
+ "donIdBytes32": "0x66756e2d706f6c79676f6e2d6d61696e6e65742d310000000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/"
+ ],
+ "chainId": "137"
+ },
+ {
+ "contractAddress": "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C",
+ "donId": "fun-polygon-mumbai-1",
+ "donIdBytes32": "0x66756e2d706f6c79676f6e2d6d756d6261692d31000000000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.testnet.chain.link/",
+ "https://02.functions-gateway.testnet.chain.link/"
+ ],
+ "chainId": "80001"
+ },
+ {
+ "contractAddress": "0x9f82a6A0758517FD0AfA463820F586999AF314a0",
+ "donId": "fun-avalanche-mainnet-1",
+ "donIdBytes32": "0x66756e2d6176616c616e6368652d6d61696e6e65742d31000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.chain.link/",
+ "https://02.functions-gateway.chain.link/"
+ ],
+ "chainId": "43114"
+ },
+ {
+ "contractAddress": "0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0",
+ "donId": "fun-avalanche-fuji-1",
+ "donIdBytes32": "0x66756e2d6176616c616e6368652d66756a692d31000000000000000000000000",
+ "gatewayUrls": [
+ "https://01.functions-gateway.testnet.chain.link/",
+ "https://02.functions-gateway.testnet.chain.link/"
+ ],
+ "chainId": "43113"
+ }
+]
diff --git a/src/sandbox/drConsumer/index.ts b/src/sandbox/directRequestConsumer/index.ts
similarity index 73%
rename from src/sandbox/drConsumer/index.ts
rename to src/sandbox/directRequestConsumer/index.ts
index 78bb049..5ce6c9f 100644
--- a/src/sandbox/drConsumer/index.ts
+++ b/src/sandbox/directRequestConsumer/index.ts
@@ -14,12 +14,13 @@ export const deploy = async (
): Promise => {
const [signer] = await hre.ethers.getSigners();
- const drConsumer = await new ChainlinkDirectRequestConsumer__factory()
- .connect(signer)
- .deploy(linkTokenAddress);
- await drConsumer.deployed();
+ const directRequestConsumer =
+ await new ChainlinkDirectRequestConsumer__factory()
+ .connect(signer)
+ .deploy(linkTokenAddress);
+ await directRequestConsumer.deployed();
- return drConsumer.address;
+ return directRequestConsumer.address;
};
export const requestData = async (
@@ -32,12 +33,12 @@ export const requestData = async (
multiplyTimes: BigNumberish
): Promise<{ transactionHash: string }> => {
const [signer] = await hre.ethers.getSigners();
- const drConsumer = ChainlinkDirectRequestConsumer__factory.connect(
+ const directRequestConsumer = ChainlinkDirectRequestConsumer__factory.connect(
directRequestConsumerAddress,
signer
);
- const tx: ContractTransaction = await drConsumer.requestData(
+ const tx: ContractTransaction = await directRequestConsumer.requestData(
operatorAddress,
externalJobID.replace(/-/g, ""),
observationURL,
@@ -59,9 +60,9 @@ export const getLatestAnswer = async (
directRequestConsumerAddress: string
): Promise => {
const [signer] = await hre.ethers.getSigners();
- const drConsumer = ChainlinkDirectRequestConsumer__factory.connect(
+ const directRequestConsumer = ChainlinkDirectRequestConsumer__factory.connect(
directRequestConsumerAddress,
signer
);
- return drConsumer.answer();
+ return directRequestConsumer.answer();
};
diff --git a/src/sandbox/functionsConsumer/index.ts b/src/sandbox/functionsConsumer/index.ts
new file mode 100644
index 0000000..0f7ea9c
--- /dev/null
+++ b/src/sandbox/functionsConsumer/index.ts
@@ -0,0 +1,246 @@
+import { Location } from "@chainlink/functions-toolkit/dist/types";
+import { BigNumberish, Contract, ContractTransaction, Signer } from "ethers";
+import { HardhatRuntimeEnvironment } from "hardhat/types";
+
+import { FunctionsConsumer__factory } from "../../../types";
+import { Overrides } from "../../shared/types";
+
+export const deploy = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsRouterAddress: string,
+ donId: string,
+ overrides?: Overrides
+): Promise => {
+ const functionsConsumer = await FunctionsConsumer.deploy({
+ hre,
+ functionsRouterAddress,
+ donId,
+ overrides,
+ });
+ return functionsConsumer.functionsConsumerAddress;
+};
+
+export const sendRequest = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string,
+ subscriptionId: BigNumberish,
+ source: string,
+ encryptedSecretsReference: string,
+ secretsLocation: Location,
+ args?: string[],
+ bytesArgs?: string[],
+ callbackGasLimit?: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ overrides,
+ });
+ return functionsConsumer.sendRequest(
+ subscriptionId,
+ source,
+ encryptedSecretsReference,
+ secretsLocation,
+ args,
+ bytesArgs,
+ callbackGasLimit
+ );
+};
+
+export const sendEncodedRequest = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string,
+ subscriptionId: BigNumberish,
+ encodedRequest: string,
+ callbackGasLimit: BigNumberish,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ overrides,
+ });
+ return functionsConsumer.sendEncodedRequest(
+ encodedRequest,
+ subscriptionId,
+ callbackGasLimit
+ );
+};
+
+export const setDonId = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string,
+ donId: string,
+ overrides?: Overrides
+): Promise<{ transactionHash: string }> => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ overrides,
+ });
+ return functionsConsumer.setDonId(donId);
+};
+
+export const getDonId = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string
+): Promise => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ });
+ return functionsConsumer.getDonId();
+};
+
+export const getLastRequestId = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string
+): Promise => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ });
+ return functionsConsumer.getLastRequestId();
+};
+
+export const getLastResponse = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string
+): Promise => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ });
+ return functionsConsumer.getLastResponse();
+};
+
+export const getLastError = async (
+ hre: HardhatRuntimeEnvironment,
+ functionsConsumerAddress: string
+): Promise => {
+ const functionsConsumer = await FunctionsConsumer.initialize({
+ hre,
+ functionsConsumerAddress,
+ });
+ return functionsConsumer.getLastError();
+};
+
+export class FunctionsConsumer {
+ private hre: HardhatRuntimeEnvironment;
+ private functionsConsumerContract: Contract;
+ public functionsConsumerAddress: string;
+
+ private constructor(
+ hre: HardhatRuntimeEnvironment,
+ signer: Signer,
+ functionsConsumerAddress: string
+ ) {
+ this.hre = hre;
+ this.functionsConsumerContract = FunctionsConsumer__factory.connect(
+ functionsConsumerAddress,
+ signer
+ );
+ this.functionsConsumerAddress = this.functionsConsumerContract.address;
+ }
+
+ static async initialize(args: {
+ hre: HardhatRuntimeEnvironment;
+ functionsConsumerAddress: string;
+ overrides?: Overrides;
+ }): Promise {
+ const { hre, functionsConsumerAddress, overrides } = args;
+ const accounts = await hre.ethers.getSigners();
+ const signer = overrides?.signer || accounts[0];
+ return new FunctionsConsumer(hre, signer, functionsConsumerAddress);
+ }
+
+ static async deploy(args: {
+ hre: HardhatRuntimeEnvironment;
+ functionsRouterAddress: string;
+ donId: string;
+ overrides?: Overrides;
+ }): Promise {
+ const { hre, functionsRouterAddress, donId, overrides } = args;
+ const accounts = await hre.ethers.getSigners();
+ const signer = overrides?.signer || accounts[0];
+ const functionsConsumer = await new FunctionsConsumer__factory()
+ .connect(signer)
+ .deploy(
+ functionsRouterAddress,
+ hre.ethers.utils.formatBytes32String(donId)
+ );
+ await functionsConsumer.deployed();
+ return new FunctionsConsumer(hre, signer, functionsConsumer.address);
+ }
+
+ async sendRequest(
+ subscriptionId: BigNumberish,
+ source: string,
+ encryptedSecretsReference: string,
+ secretsLocation: Location,
+ args: string[] = [],
+ bytesArgs: string[] = [],
+ callbackGasLimit: BigNumberish = 100_000
+ ): Promise<{ transactionHash: string }> {
+ const tx: ContractTransaction =
+ await this.functionsConsumerContract.sendRequest(
+ source,
+ secretsLocation,
+ encryptedSecretsReference,
+ args,
+ bytesArgs,
+ subscriptionId,
+ callbackGasLimit,
+ {
+ gasLimit: 500_000,
+ }
+ );
+ await tx.wait(this.hre.config.chainlink.confirmations);
+
+ return { transactionHash: tx.hash };
+ }
+
+ async sendEncodedRequest(
+ encodedRequest: string,
+ subscriptionId: BigNumberish,
+ callbackGasLimit: BigNumberish
+ ): Promise<{ transactionHash: string }> {
+ const tx: ContractTransaction =
+ await this.functionsConsumerContract.sendEncodedRequest(
+ encodedRequest,
+ subscriptionId,
+ callbackGasLimit
+ );
+ await tx.wait(this.hre.config.chainlink.confirmations);
+
+ return { transactionHash: tx.hash };
+ }
+
+ async setDonId(donId: string): Promise<{ transactionHash: string }> {
+ const tx: ContractTransaction =
+ await this.functionsConsumerContract.setDonId(
+ this.hre.ethers.utils.formatBytes32String(donId)
+ );
+ await tx.wait(this.hre.config.chainlink.confirmations);
+
+ return { transactionHash: tx.hash };
+ }
+
+ async getDonId(): Promise {
+ const donId = this.functionsConsumerContract.donId();
+ return this.hre.ethers.utils.parseBytes32String(donId);
+ }
+
+ async getLastRequestId(): Promise {
+ return this.functionsConsumerContract.s_lastRequestId();
+ }
+
+ async getLastResponse(): Promise {
+ return this.functionsConsumerContract.s_lastResponse();
+ }
+
+ async getLastError(): Promise {
+ return this.functionsConsumerContract.s_lastError();
+ }
+}
diff --git a/src/sandbox/functionsSimulations/index.ts b/src/sandbox/functionsSimulations/index.ts
new file mode 100644
index 0000000..ed70750
--- /dev/null
+++ b/src/sandbox/functionsSimulations/index.ts
@@ -0,0 +1,15 @@
+import * as functionsToolkit from "@chainlink/functions-toolkit";
+
+export const simulateRequest = async (
+ source: string,
+ secrets: Record,
+ args: string[],
+ bytesArgs: string[]
+): Promise => {
+ return functionsToolkit.simulateScript({
+ source,
+ secrets,
+ args,
+ bytesArgs,
+ });
+};
diff --git a/src/sandbox/node/index.ts b/src/sandbox/node/index.ts
index 48fd3d2..50bfff7 100644
--- a/src/sandbox/node/index.ts
+++ b/src/sandbox/node/index.ts
@@ -4,13 +4,13 @@ import * as fs from "fs";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { join } from "path";
-import { CHAINLINK_NODE_CONTAINER } from "../../shared/constants";
+import * as constants from "../../shared/constants";
import { NodeStartType } from "../../shared/enums";
import { DockerOutput } from "../../shared/types";
-const login = async (hre: HardhatRuntimeEnvironment): Promise => {
+const login = async (): Promise => {
await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "admin", "login", "-f", "/clroot/api-credentials"],
{
cwd: join("./"),
@@ -39,10 +39,10 @@ const up = async (
// Replace placeholders with values
const config = configTemplate
- .replace("{{CHAIN_ID}}", node.chain_id)
- .replace("{{CHAIN_NAME}}", node.chain_name)
- .replace("{{CHAIN_WSURL}}", node.ws_url)
- .replace("{{CHAIN_HTTPURL}}", node.http_url);
+ .replace("{{CHAIN_ID}}", node?.chain_id || constants.DEFAULT_CHAIN_ID)
+ .replace("{{CHAIN_NAME}}", node?.chain_name || constants.DEFAULT_CHAIN_NAME)
+ .replace("{{CHAIN_WSURL}}", node?.ws_url || constants.DEFAULT_WS_URL)
+ .replace("{{CHAIN_HTTPURL}}", node?.http_url || constants.DEFAULT_HTTP_URL);
// Write the replaced content to a new TOML file
try {
@@ -59,18 +59,27 @@ const up = async (
// Replace placeholders with values
const dockerCompose = dockerComposeTemplate
- .replace("{{PG_USER}}", node.pg_user)
- .replace("{{PG_USER}}", node.pg_user)
- .replace("{{PG_USER}}", node.pg_user)
- .replace("{{PG_PASSWORD}}", node.pg_password)
- .replace("{{PG_PASSWORD}}", node.pg_password)
- .replace("{{PG_DB}}", node.pg_db)
- .replace("{{PG_DB}}", node.pg_db)
- .replace("{{CL_PASSWORD_KEYSTORE}}", node.cl_keystore_password);
+ .replace("{{PG_USER}}", node?.pg_user || constants.DEFAULT_PG_USER)
+ .replace("{{PG_USER}}", node?.pg_user || constants.DEFAULT_PG_USER)
+ .replace("{{PG_USER}}", node?.pg_user || constants.DEFAULT_PG_USER)
+ .replace(
+ "{{PG_PASSWORD}}",
+ node?.pg_password || constants.DEFAULT_PG_PASSWORD
+ )
+ .replace(
+ "{{PG_PASSWORD}}",
+ node?.pg_password || constants.DEFAULT_PG_PASSWORD
+ )
+ .replace("{{PG_DB}}", node?.pg_db || constants.DEFAULT_PG_DB)
+ .replace("{{PG_DB}}", node?.pg_db || constants.DEFAULT_PG_DB)
+ .replace(
+ "{{CL_PASSWORD_KEYSTORE}}",
+ node?.cl_keystore_password || constants.DEFAULT_CL_KEYSTORE_PASSWORD
+ );
// Write the replaced content to a new YAML file
try {
- await fs.writeFileSync(
+ fs.writeFileSync(
join("./docker-compose-chainlink-hardhat.yaml"),
dockerCompose,
"utf8"
@@ -80,15 +89,13 @@ const up = async (
}
// Combine the strings with a line break
- const apiCredentials = `${node.cl_api_user}\n${node.cl_api_password}`;
+ const apiCredentials = `${
+ node?.cl_api_user || constants.DEFAULT_CL_API_USER
+ }\n${node?.cl_api_password || constants.DEFAULT_CL_API_PASSWORD}`;
// Write the content to a file
try {
- await fs.writeFileSync(
- join("./clroot/api-credentials"),
- apiCredentials,
- "utf8"
- );
+ fs.writeFileSync(join("./clroot/api-credentials"), apiCredentials, "utf8");
} catch (e: any) {
throw new Error(e);
}
@@ -153,9 +160,9 @@ export const stop = async (
export const getETHKeys = async (
hre: HardhatRuntimeEnvironment
): Promise => {
- await login(hre);
+ await login();
const result = await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "-j", "keys", "eth", "list"],
{
cwd: join("./"),
@@ -168,9 +175,9 @@ export const getETHKeys = async (
export const getP2PKeys = async (
hre: HardhatRuntimeEnvironment
): Promise => {
- await login(hre);
+ await login();
const result = await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "-j", "keys", "p2p", "list"],
{
cwd: join("./"),
@@ -180,12 +187,27 @@ export const getP2PKeys = async (
return result.out;
};
+export const getVRFKeys = async (
+ hre: HardhatRuntimeEnvironment
+): Promise => {
+ await login();
+ const result = await compose.exec(
+ constants.CHAINLINK_NODE_CONTAINER,
+ ["chainlink", "-j", "keys", "vrf", "list"],
+ {
+ cwd: join("./"),
+ composeOptions: ["-f", "docker-compose-chainlink-hardhat.yaml"],
+ }
+ );
+ return result.out;
+};
+
export const getOCRKeys = async (
hre: HardhatRuntimeEnvironment
): Promise => {
- await login(hre);
+ await login();
const result = await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "-j", "keys", "ocr", "list"],
{
cwd: join("./"),
@@ -198,9 +220,9 @@ export const getOCRKeys = async (
export const getJobs = async (
hre: HardhatRuntimeEnvironment
): Promise => {
- await login(hre);
+ await login();
const result = await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "-j", "jobs", "list"],
{
cwd: join("./"),
@@ -238,10 +260,10 @@ export const createDirectRequestJob = async (
throw new Error(e);
}
- await login(hre);
+ await login();
await compose.exec(
- CHAINLINK_NODE_CONTAINER,
+ constants.CHAINLINK_NODE_CONTAINER,
["chainlink", "jobs", "create", "/clroot/jobs/direct-request-job.toml"],
{
cwd: join("./"),
diff --git a/src/shared/constants.ts b/src/shared/constants.ts
index dc469d4..7f7fb46 100644
--- a/src/shared/constants.ts
+++ b/src/shared/constants.ts
@@ -2,3 +2,14 @@ export const PACKAGE_NAME = "chainlink";
export const CHAINLINK_NODE_CONTAINER = "chainlink_node";
export const SUBTASK_PADDING = 30;
export const AGGREGATOR_PROXY_PHASE_OFFSET = 64;
+export const DEFAULT_PORT = 8546;
+export const DEFAULT_CHAIN_ID = "1337";
+export const DEFAULT_CHAIN_NAME = "local";
+export const DEFAULT_HTTP_URL = "http://host.docker.internal:8545";
+export const DEFAULT_WS_URL = "ws://host.docker.internal:8545";
+export const DEFAULT_CL_KEYSTORE_PASSWORD = "password1234567890";
+export const DEFAULT_CL_API_USER = "user@chain.link";
+export const DEFAULT_CL_API_PASSWORD = "password1234567890";
+export const DEFAULT_PG_USER = "chainlink";
+export const DEFAULT_PG_PASSWORD = "password1234567890";
+export const DEFAULT_PG_DB = "chainlink";
diff --git a/src/shared/enums.ts b/src/shared/enums.ts
index d2804dc..e500072 100644
--- a/src/shared/enums.ts
+++ b/src/shared/enums.ts
@@ -12,8 +12,9 @@ export enum Task {
utils = "utils",
node = "sandbox:node",
operator = "sandbox:operator",
- drConsumer = "sandbox:drConsumer",
+ directRequestConsumer = "sandbox:directRequestConsumer",
linkToken = "sandbox:linkToken",
+ functionsSimulation = "sandbox:functionsSimulation",
}
export enum DataFeedSubtask {
@@ -116,6 +117,19 @@ export enum AutomationRegistrarSubtask {
getTypeAndVersion = "getTypeAndVersion",
}
+export enum FunctionsSubtask {
+ createSubscription = "createSubscription",
+ fundSubscription = "fundSubscription",
+ getSubscriptionDetails = "getSubscriptionDetails",
+ cancelSubscription = "cancelSubscription",
+ requestSubscriptionOwnerTransfer = "requestSubscriptionOwnerTransfer",
+ acceptSubscriptionOwnerTransfer = "acceptSubscriptionOwnerTransfer",
+ addConsumer = "addConsumer",
+ removeConsumer = "removeConsumer",
+ timeoutRequests = "timeoutRequests",
+ estimateRequestCost = "estimateRequestCost",
+}
+
export enum PluginRegistriesSubtask {
getDataFeed = "getDataFeed",
getFeedRegistry = "getFeedRegistry",
@@ -123,7 +137,7 @@ export enum PluginRegistriesSubtask {
getLinkToken = "getLinkToken",
getKeeperRegistry = "getKeeperRegistry",
getL2Sequencer = "getL2Sequencer",
- getFunctionOracle = "getFunctionOracle",
+ getFunctionRouter = "getFunctionRouter",
getDenomination = "getDenomination",
}
@@ -143,7 +157,7 @@ export enum OperatorSubtask {
setAuthorizedSender = "setAuthorizedSender",
}
-export enum DRConsumerSubtask {
+export enum DirectRequestConsumerSubtask {
deploy = "deploy",
requestData = "requestData",
getLatestAnswer = "getLatestAnswer",
@@ -157,10 +171,16 @@ export enum LinkTokenSubtask {
decreaseApproval = "decreaseApproval",
}
+export enum FunctionsSimulationSubtask {
+ simulateRequest = "simulateRequest",
+}
+
export enum UtilsSubtask {
getRoundId = "getRoundId",
parseRoundId = "parseRoundId",
transferETH = "transferETH",
+ createGist = "createGist",
+ deleteGist = "deleteGist",
}
export const enum InquirableParameter {
@@ -172,7 +192,11 @@ export const enum InquirableParameter {
keeperRegistryAddress = "keeperRegistryAddress",
keeperRegistrarAddress = "keeperRegistrarAddress",
l2SequencerAddress = "l2SequencerAddress",
- functionOracleAddress = "functionOracleAddress",
+ functionsRouterAddress = "functionsRouterAddress",
+ donId = "donId",
+ secretsLocation = "secretsLocation",
+ codeLocation = "codeLocation",
+ codeLanguage = "codeLanguage",
feedRegistryBaseTick = "feedRegistryBaseTick",
feedRegistryQuoteTick = "feedRegistryQuoteTick",
}
diff --git a/src/shared/types.ts b/src/shared/types.ts
index 95b8bce..c1f06e2 100644
--- a/src/shared/types.ts
+++ b/src/shared/types.ts
@@ -1,7 +1,9 @@
+import { BigNumber, providers, Signer } from "ethers";
+
import {
DataFeed,
FeedRegistry,
- FunctionOracle,
+ FunctionsRouter,
KeeperRegistry,
L2Sequencer,
LinkToken,
@@ -21,7 +23,7 @@ export type LinkTokensRegistry = Record;
export type KeeperRegistriesRegistry = Record;
export type L2SequencersRegistry = Record;
export type DenominationsRegistry = Record;
-export type FunctionOraclesRegistry = Record;
+export type FunctionsRoutersRegistry = Record;
export type Subtasks = Record>;
@@ -37,3 +39,24 @@ export type DockerOutput = {
exitCode: number | null;
err: string;
};
+
+export type FunctionsSubscriptionDetails = {
+ balance: BigNumber;
+ owner: string;
+ blockedBalance: BigNumber;
+ proposedOwner: string;
+ consumers: string[];
+ flags: string;
+};
+
+export type VRFSubscriptionDetails = {
+ balance: BigNumber;
+ reqCount: BigNumber;
+ owner: string;
+ consumers: string[];
+};
+
+export type Overrides = {
+ signer?: Signer;
+ provider?: providers.JsonRpcProvider;
+};
diff --git a/src/subtasks/helpers/index.ts b/src/subtasks/helpers/index.ts
index df8840b..0a2ac03 100644
--- a/src/subtasks/helpers/index.ts
+++ b/src/subtasks/helpers/index.ts
@@ -15,7 +15,7 @@ export const registerSubtasks = () => {
`${PACKAGE_NAME}:${taskName}:${subtaskName}`
);
subtaskProperties.args.forEach((subtaskArg) => {
- if (subtaskArg.defaultValue) {
+ if (subtaskArg.defaultValue !== undefined) {
subtaskDefinition.addOptionalParam(
subtaskArg.name,
subtaskArg.description,
diff --git a/src/subtasks/index.ts b/src/subtasks/index.ts
index b51af72..3d86d5a 100644
--- a/src/subtasks/index.ts
+++ b/src/subtasks/index.ts
@@ -4,9 +4,11 @@ import {
AutomationRegistrySubtask,
DataFeedProxySubtask,
DataFeedSubtask,
- DRConsumerSubtask,
+ DirectRequestConsumerSubtask,
ENSFeedsResolverSubtask,
FeedRegistrySubtask,
+ FunctionsSimulationSubtask,
+ FunctionsSubtask,
L2SequencerSubtask,
LinkTokenSubtask,
NodeSubtask,
@@ -24,8 +26,10 @@ import * as dataFeedProxyActions from "../tasks/feeds/dataFeedProxy";
import * as ensFeedsResolverActions from "../tasks/feeds/ensFeedsResolver";
import * as feedRegistryActions from "../tasks/feeds/feedRegistry";
import * as l2FeedUptimeSequencerActions from "../tasks/feeds/l2FeedUptimeSequencer";
+import * as functionsActions from "../tasks/functions";
import * as registriesActions from "../tasks/registries";
-import * as drConsumerActions from "../tasks/sandbox/drConsumer";
+import * as directRequestConsumerActions from "../tasks/sandbox/directRequestConsumer";
+import * as functionsSimulationActions from "../tasks/sandbox/functionsSimulation";
import * as linkTokenActions from "../tasks/sandbox/linkToken";
import * as nodeActions from "../tasks/sandbox/node";
import * as operatorActions from "../tasks/sandbox/operator";
@@ -807,7 +811,7 @@ export const subtasks: Subtasks = {
},
{
name: "newOwnerAddress",
- description: "Address of new Subscription owner",
+ description: "Address of new owner of Subscription",
},
],
},
@@ -1207,7 +1211,205 @@ export const subtasks: Subtasks = {
],
},
},
- [Task.functions]: {},
+ [Task.functions]: {
+ [FunctionsSubtask.createSubscription]: {
+ action: functionsActions.createSubscription,
+ description: camelToFlat(FunctionsSubtask.createSubscription),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "consumerAddress",
+ description: "Address of Functions Consumer",
+ defaultValue: "",
+ },
+ ],
+ },
+ [FunctionsSubtask.fundSubscription]: {
+ action: functionsActions.fundSubscription,
+ description: camelToFlat(FunctionsSubtask.fundSubscription),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "linkTokenAddress",
+ description: "Address of Link Token",
+ },
+ {
+ name: "amountInJuels",
+ description: "Amount of LINK in Juels to fund Subscription",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ ],
+ },
+ [FunctionsSubtask.cancelSubscription]: {
+ action: functionsActions.cancelSubscription,
+ description: camelToFlat(FunctionsSubtask.cancelSubscription),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ {
+ name: "receivingAddress",
+ description: "Address to receive the balance of Subscription",
+ defaultValue: "",
+ },
+ ],
+ },
+ [FunctionsSubtask.getSubscriptionDetails]: {
+ action: functionsActions.getSubscriptionDetails,
+ description: camelToFlat(FunctionsSubtask.getSubscriptionDetails),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ ],
+ },
+ [FunctionsSubtask.requestSubscriptionOwnerTransfer]: {
+ action: functionsActions.requestSubscriptionOwnerTransfer,
+ description: camelToFlat(
+ FunctionsSubtask.requestSubscriptionOwnerTransfer
+ ),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ {
+ name: "newOwnerAddress",
+ description: "Address of new owner of Subscription ",
+ },
+ ],
+ },
+ [FunctionsSubtask.acceptSubscriptionOwnerTransfer]: {
+ action: functionsActions.acceptSubscriptionOwnerTransfer,
+ description: camelToFlat(
+ FunctionsSubtask.acceptSubscriptionOwnerTransfer
+ ),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ ],
+ },
+ [FunctionsSubtask.addConsumer]: {
+ action: functionsActions.addConsumer,
+ description: camelToFlat(FunctionsSubtask.addConsumer),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "consumerAddress",
+ description: "Address of Functions Consumer",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ ],
+ },
+ [FunctionsSubtask.removeConsumer]: {
+ action: functionsActions.removeConsumer,
+ description: camelToFlat(FunctionsSubtask.removeConsumer),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "consumerAddress",
+ description: "Address of Functions Consumer",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ ],
+ },
+ [FunctionsSubtask.timeoutRequests]: {
+ action: functionsActions.timeoutRequests,
+ description: camelToFlat(FunctionsSubtask.timeoutRequests),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "requestIdsString",
+ description: "Comma-separated requests IDs",
+ },
+ {
+ name: "donId",
+ description: "ID of the DON where Functions requests has been sent",
+ },
+ {
+ name: "toBlock",
+ description: "End block in search range",
+ defaultValue: "latest",
+ },
+ {
+ name: "pastBlocksToSearch",
+ description: "Number of blocks to search (before toBlock)",
+ defaultValue: "1000",
+ },
+ ],
+ },
+ [FunctionsSubtask.estimateRequestCost]: {
+ action: functionsActions.estimateRequestCost,
+ description: camelToFlat(FunctionsSubtask.estimateRequestCost),
+ args: [
+ {
+ name: "functionsRouterAddress",
+ description: "Address of Functions Router",
+ },
+ {
+ name: "donId",
+ description:
+ "ID of the DON to which the Functions request will be sent",
+ },
+ {
+ name: "subscriptionId",
+ description: "Subscription ID",
+ },
+ {
+ name: "callbackGasLimit",
+ description: "Total gas used by the consumer contract's callback",
+ },
+ {
+ name: "gasPriceWei",
+ description: "Gas price in wei",
+ },
+ ],
+ },
+ },
[Task.registries]: {
[PluginRegistriesSubtask.getDataFeed]: {
action: registriesActions.getDataFeed,
@@ -1239,9 +1441,9 @@ export const subtasks: Subtasks = {
description: camelToFlat(PluginRegistriesSubtask.getL2Sequencer),
args: [],
},
- [PluginRegistriesSubtask.getFunctionOracle]: {
- action: registriesActions.getFunctionOracle,
- description: camelToFlat(PluginRegistriesSubtask.getFunctionOracle),
+ [PluginRegistriesSubtask.getFunctionRouter]: {
+ action: registriesActions.getFunctionRouter,
+ description: camelToFlat(PluginRegistriesSubtask.getFunctionRouter),
args: [],
},
[PluginRegistriesSubtask.getDenomination]: {
@@ -1290,6 +1492,34 @@ export const subtasks: Subtasks = {
},
],
},
+ [UtilsSubtask.createGist]: {
+ action: utilsActions.createGist,
+ description: "Create private GitHub gist",
+ args: [
+ {
+ name: "githubApiToken",
+ description: "GitHub API Token",
+ },
+ {
+ name: "content",
+ description: "Gist content",
+ },
+ ],
+ },
+ [UtilsSubtask.deleteGist]: {
+ action: utilsActions.deleteGist,
+ description: "Delete private GitHub gist",
+ args: [
+ {
+ name: "githubApiToken",
+ description: "GitHub API Token",
+ },
+ {
+ name: "gistURL",
+ description: "Gist URL",
+ },
+ ],
+ },
},
[Task.node]: {
[NodeSubtask.run]: {
@@ -1445,9 +1675,9 @@ export const subtasks: Subtasks = {
],
},
},
- [Task.drConsumer]: {
- [DRConsumerSubtask.deploy]: {
- action: drConsumerActions.deploy,
+ [Task.directRequestConsumer]: {
+ [DirectRequestConsumerSubtask.deploy]: {
+ action: directRequestConsumerActions.deploy,
description: "Deploy Direct Request Consumer",
args: [
{
@@ -1456,8 +1686,8 @@ export const subtasks: Subtasks = {
},
],
},
- [DRConsumerSubtask.requestData]: {
- action: drConsumerActions.requestData,
+ [DirectRequestConsumerSubtask.requestData]: {
+ action: directRequestConsumerActions.requestData,
description: "Request Data with Direct Request",
args: [
{
@@ -1487,8 +1717,8 @@ export const subtasks: Subtasks = {
},
],
},
- [DRConsumerSubtask.getLatestAnswer]: {
- action: drConsumerActions.getLatestAnswer,
+ [DirectRequestConsumerSubtask.getLatestAnswer]: {
+ action: directRequestConsumerActions.getLatestAnswer,
description: "Get latest answer",
args: [
{
@@ -1498,4 +1728,26 @@ export const subtasks: Subtasks = {
],
},
},
+ [Task.functionsSimulation]: {
+ [FunctionsSimulationSubtask.simulateRequest]: {
+ action: functionsSimulationActions.simulateRequest,
+ description: "Simulate Functions request",
+ args: [
+ {
+ name: "source",
+ description: "Source code to execute",
+ },
+ {
+ name: "args",
+ description: "Comma-separated request args",
+ defaultValue: "",
+ },
+ {
+ name: "bytesArgs",
+ description: "Comma-separated request bytes args",
+ defaultValue: "",
+ },
+ ],
+ },
+ },
};
diff --git a/src/tasks/automation/keeperRegistry.ts b/src/tasks/automation/keeperRegistry.ts
index b0d79bd..618055a 100644
--- a/src/tasks/automation/keeperRegistry.ts
+++ b/src/tasks/automation/keeperRegistry.ts
@@ -104,7 +104,7 @@ export const migrateUpkeeps: ActionType<{
destination: string;
}> = async (taskArgs, hre) => {
const upkeepIds = taskArgs.upkeepIds.split(",").map((upkeepId) => {
- return BigNumber.from(upkeepId);
+ return BigNumber.from(upkeepId.trim());
});
return automation.migrateUpkeeps(
hre,
diff --git a/src/tasks/functions/index.ts b/src/tasks/functions/index.ts
index e69de29..d1644c8 100644
--- a/src/tasks/functions/index.ts
+++ b/src/tasks/functions/index.ts
@@ -0,0 +1,155 @@
+import { RequestCommitment } from "@chainlink/functions-toolkit/dist/types";
+import { BigNumber, BigNumberish } from "ethers";
+import { ActionType } from "hardhat/types";
+
+import * as functions from "../../functions";
+import { FunctionsSubscriptionDetails } from "../../shared/types";
+
+export const createSubscription: ActionType<{
+ functionsRouterAddress: string;
+ consumerAddress?: string;
+}> = async (taskArgs, hre): Promise<{ subscriptionId: BigNumber }> => {
+ return functions.createSubscription(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.consumerAddress
+ );
+};
+
+export const fundSubscription: ActionType<{
+ linkTokenAddress: string;
+ functionsRouterAddress: string;
+ amountInJuels: BigNumberish;
+ subscriptionId: BigNumberish;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.fundSubscription(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.linkTokenAddress,
+ taskArgs.amountInJuels,
+ taskArgs.subscriptionId
+ );
+};
+
+export const getSubscriptionDetails: ActionType<{
+ functionsRouterAddress: string;
+ subscriptionId: BigNumberish;
+}> = async (taskArgs, hre): Promise => {
+ return functions.getSubscriptionDetails(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.subscriptionId
+ );
+};
+
+export const cancelSubscription: ActionType<{
+ functionsRouterAddress: string;
+ subscriptionId: BigNumberish;
+ receivingAddress: string | undefined;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.cancelSubscription(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.subscriptionId,
+ taskArgs.receivingAddress
+ );
+};
+
+export const requestSubscriptionOwnerTransfer: ActionType<{
+ functionsRouterAddress: string;
+ subscriptionId: BigNumberish;
+ newOwnerAddress: string;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.requestSubscriptionOwnerTransfer(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.subscriptionId,
+ taskArgs.newOwnerAddress
+ );
+};
+
+export const acceptSubscriptionOwnerTransfer: ActionType<{
+ functionsRouterAddress: string;
+ subscriptionId: string;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.acceptSubscriptionOwnerTransfer(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.subscriptionId
+ );
+};
+
+export const addConsumer: ActionType<{
+ functionsRouterAddress: string;
+ consumerAddress: string;
+ subscriptionId: BigNumberish;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.addConsumer(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.consumerAddress,
+ taskArgs.subscriptionId
+ );
+};
+
+export const removeConsumer: ActionType<{
+ functionsRouterAddress: string;
+ consumerAddress: string;
+ subscriptionId: BigNumberish;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ return functions.removeConsumer(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.consumerAddress,
+ taskArgs.subscriptionId
+ );
+};
+
+export const timeoutRequests: ActionType<{
+ functionsRouterAddress: string;
+ requestIdsString: string;
+ donId: string;
+ toBlock?: string;
+ pastBlocksToSearch?: string;
+}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
+ const requestIds = taskArgs.requestIdsString
+ .split(",")
+ .map((value) => value.trim());
+ const toBlock = taskArgs.toBlock ? +taskArgs.toBlock : "latest";
+ const pastBlocksToSearch = taskArgs.pastBlocksToSearch
+ ? +taskArgs.pastBlocksToSearch
+ : undefined;
+ const promises = requestIds.map((requestId: string) => {
+ return functions.fetchRequestCommitment(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.donId,
+ requestId,
+ toBlock,
+ pastBlocksToSearch
+ );
+ });
+ const requestCommitments: RequestCommitment[] = await Promise.all(promises);
+ return functions.timeoutRequests(
+ hre,
+ taskArgs.functionsRouterAddress,
+ requestCommitments
+ );
+};
+
+export const estimateRequestCost: ActionType<{
+ functionsRouterAddress: string;
+ donId: string;
+ subscriptionId: BigNumberish;
+ callbackGasLimit: string;
+ gasPriceWei: BigNumberish;
+}> = async (taskArgs, hre): Promise => {
+ return functions.estimateRequestCost(
+ hre,
+ taskArgs.functionsRouterAddress,
+ taskArgs.donId,
+ taskArgs.subscriptionId,
+ +taskArgs.callbackGasLimit,
+ taskArgs.gasPriceWei
+ );
+};
diff --git a/src/tasks/registries/index.ts b/src/tasks/registries/index.ts
index 1c5ae14..0e0c799 100644
--- a/src/tasks/registries/index.ts
+++ b/src/tasks/registries/index.ts
@@ -26,8 +26,8 @@ export const getL2Sequencer: ActionType<{}> = async (taskArgs, hre) => {
return inquirers.inquireL2Sequencer(hre, false);
};
-export const getFunctionOracle: ActionType<{}> = async (taskArgs, hre) => {
- return inquirers.inquireFunctionOracle(hre, false);
+export const getFunctionRouter: ActionType<{}> = async (taskArgs, hre) => {
+ return inquirers.inquireFunctionsRouter(hre, false);
};
export const getDenomination: ActionType<{}> = async () => {
diff --git a/src/tasks/sandbox/drConsumer/index.ts b/src/tasks/sandbox/directRequestConsumer/index.ts
similarity index 72%
rename from src/tasks/sandbox/drConsumer/index.ts
rename to src/tasks/sandbox/directRequestConsumer/index.ts
index 12a56ba..2e6795b 100644
--- a/src/tasks/sandbox/drConsumer/index.ts
+++ b/src/tasks/sandbox/directRequestConsumer/index.ts
@@ -1,12 +1,12 @@
import { BigNumber } from "ethers";
import { ActionType } from "hardhat/types";
-import * as drConsumer from "../../../sandbox/drConsumer";
+import * as directRequestConsumer from "../../../sandbox/directRequestConsumer";
export const deploy: ActionType<{
linkTokenAddress: string;
}> = async (taskArgs, hre): Promise => {
- return drConsumer.deploy(hre, taskArgs.linkTokenAddress);
+ return directRequestConsumer.deploy(hre, taskArgs.linkTokenAddress);
};
export const requestData: ActionType<{
@@ -17,7 +17,7 @@ export const requestData: ActionType<{
pathToData: string;
multiplyTimes: string;
}> = async (taskArgs, hre): Promise<{ transactionHash: string }> => {
- return drConsumer.requestData(
+ return directRequestConsumer.requestData(
hre,
taskArgs.directRequestConsumerAddress,
taskArgs.operatorAddress,
@@ -31,5 +31,8 @@ export const requestData: ActionType<{
export const getLatestAnswer: ActionType<{
directRequestConsumerAddress: string;
}> = async (taskArgs, hre): Promise => {
- return drConsumer.getLatestAnswer(hre, taskArgs.directRequestConsumerAddress);
+ return directRequestConsumer.getLatestAnswer(
+ hre,
+ taskArgs.directRequestConsumerAddress
+ );
};
diff --git a/src/tasks/sandbox/functionsSimulation/index.ts b/src/tasks/sandbox/functionsSimulation/index.ts
new file mode 100644
index 0000000..145de32
--- /dev/null
+++ b/src/tasks/sandbox/functionsSimulation/index.ts
@@ -0,0 +1,25 @@
+import { ActionType } from "hardhat/types";
+
+import * as functionsSimulations from "../../../sandbox/functionsSimulations";
+
+export const simulateRequest: ActionType<{
+ source: string;
+ secrets?: string;
+ args?: string;
+ bytesArgs?: string;
+}> = async (taskArgs, hre): Promise => {
+ const secrets = taskArgs.secrets ? JSON.parse(taskArgs.secrets) : {};
+ const args = taskArgs.args
+ ? taskArgs.args.split(",").map((value) => value.trim())
+ : [];
+ const bytesArgs = taskArgs.bytesArgs
+ ? taskArgs.bytesArgs.split(",").map((value) => value.trim())
+ : [];
+ const simulationResult = await functionsSimulations.simulateRequest(
+ taskArgs.source,
+ secrets,
+ args,
+ bytesArgs
+ );
+ return JSON.stringify(simulationResult);
+};
diff --git a/src/tasks/sandbox/node/index.ts b/src/tasks/sandbox/node/index.ts
index 8ca2cb9..8d83832 100644
--- a/src/tasks/sandbox/node/index.ts
+++ b/src/tasks/sandbox/node/index.ts
@@ -38,6 +38,13 @@ export const getP2PKeys: ActionType<{}> = async (
return node.getP2PKeys(hre);
};
+export const getVRFKeys: ActionType<{}> = async (
+ taskArgs,
+ hre
+): Promise => {
+ return node.getVRFKeys(hre);
+};
+
export const getOCRKeys: ActionType<{}> = async (
taskArgs,
hre
diff --git a/src/tasks/utils/index.ts b/src/tasks/utils/index.ts
index da5d848..ecd38db 100644
--- a/src/tasks/utils/index.ts
+++ b/src/tasks/utils/index.ts
@@ -22,3 +22,17 @@ export const transferETH: ActionType<{
}> = async (taskArgs, hre) => {
return utils.transferETH(hre, taskArgs.recipient, taskArgs.amount);
};
+
+export const createGist: ActionType<{
+ githubApiToken: string;
+ content: string;
+}> = async (taskArgs, _) => {
+ return utils.createGist(taskArgs.githubApiToken, taskArgs.content);
+};
+
+export const deleteGist: ActionType<{
+ githubApiToken: string;
+ content: string;
+}> = async (taskArgs, _) => {
+ return utils.deleteGist(taskArgs.githubApiToken, taskArgs.content);
+};
diff --git a/src/tasks/vrf/index.ts b/src/tasks/vrf/index.ts
index 95d6f64..d776d3a 100644
--- a/src/tasks/vrf/index.ts
+++ b/src/tasks/vrf/index.ts
@@ -2,6 +2,7 @@ import { BigNumber, BigNumberish } from "ethers";
import { BytesLike } from "ethers/lib/utils";
import { ActionType } from "hardhat/types";
+import { VRFSubscriptionDetails } from "../../shared/types";
import * as vrf from "../../vrf";
export const createSubscription: ActionType<{
@@ -41,7 +42,7 @@ export const cancelSubscription: ActionType<{
export const addConsumer: ActionType<{
vrfCoordinatorAddress: string;
consumerAddress: string;
- subscriptionId: BigNumber;
+ subscriptionId: BigNumberish;
}> = async (taskArgs, hre) => {
return vrf.addConsumer(
hre,
@@ -54,7 +55,7 @@ export const addConsumer: ActionType<{
export const removeConsumer: ActionType<{
vrfCoordinatorAddress: string;
consumerAddress: string;
- subscriptionId: BigNumber;
+ subscriptionId: BigNumberish;
}> = async (taskArgs, hre) => {
return vrf.removeConsumer(
hre,
@@ -66,8 +67,8 @@ export const removeConsumer: ActionType<{
export const getSubscriptionDetails: ActionType<{
vrfCoordinatorAddress: string;
- subscriptionId: BigNumber;
-}> = async (taskArgs, hre) => {
+ subscriptionId: BigNumberish;
+}> = async (taskArgs, hre): Promise => {
return vrf.getSubscriptionDetails(
hre,
taskArgs.vrfCoordinatorAddress,
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 11af60f..6445de6 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,3 +1,9 @@
+import * as functionsToolkit from "@chainlink/functions-toolkit";
+import { DecodedResult } from "@chainlink/functions-toolkit/dist/decodeResult";
+import {
+ FunctionsRequestParams,
+ ReturnType,
+} from "@chainlink/functions-toolkit/dist/types";
import { BigNumber, BigNumberish } from "ethers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
@@ -56,3 +62,30 @@ export const transferETH = async (
transactionHash: tx.hash,
};
};
+
+export const createGist = async (
+ githubApiToken: string,
+ content: string
+): Promise => {
+ return functionsToolkit.createGist(githubApiToken, content);
+};
+
+export const deleteGist = async (
+ githubApiToken: string,
+ gistURL: string
+): Promise => {
+ return functionsToolkit.deleteGist(githubApiToken, gistURL);
+};
+
+export const buildRequestCBOR = async (
+ requestParams: FunctionsRequestParams
+): Promise => {
+ return functionsToolkit.buildRequestCBOR(requestParams);
+};
+
+export const decodeHexString = async (
+ resultHexString: string,
+ expectedReturnType: ReturnType
+): Promise => {
+ return functionsToolkit.decodeResult(resultHexString, expectedReturnType);
+};
diff --git a/src/vrf/vrfCoordinator.ts b/src/vrf/vrfCoordinator.ts
index 3d8f69e..4bc89fc 100644
--- a/src/vrf/vrfCoordinator.ts
+++ b/src/vrf/vrfCoordinator.ts
@@ -11,6 +11,7 @@ import {
LinkTokenInterface__factory,
VRFCoordinatorV2__factory,
} from "../../types";
+import { VRFSubscriptionDetails } from "../shared/types";
export const createSubscription = async (
hre: HardhatRuntimeEnvironment,
@@ -152,12 +153,7 @@ export const getSubscriptionDetails = async (
hre: HardhatRuntimeEnvironment,
vrfCoordinatorAddress: string,
subscriptionId: BigNumberish
-): Promise<{
- balance: BigNumber;
- reqCount: BigNumber;
- owner: string;
- consumers: string[];
-}> => {
+): Promise => {
const [signer] = await hre.ethers.getSigners();
const vrfCoordinatorV2 = VRFCoordinatorV2__factory.connect(
vrfCoordinatorAddress,
diff --git a/test/automationRegistry.test.ts b/test/automationRegistry.test.ts
index dc45fb5..35ddc04 100644
--- a/test/automationRegistry.test.ts
+++ b/test/automationRegistry.test.ts
@@ -208,7 +208,7 @@ describe("Test chainlink:automationRegistry module", function () {
const {
target,
balance,
- lastAutomationNode,
+ lastKeeper,
admin,
maxValidBlocknumber,
amountSpent,
@@ -221,7 +221,7 @@ describe("Test chainlink:automationRegistry module", function () {
expect(target).to.eq(this.upkeep.address);
expect(balance.toNumber()).to.eq(0);
- expect(lastAutomationNode).to.eq(this.hre.ethers.constants.AddressZero);
+ expect(lastKeeper).to.eq(this.hre.ethers.constants.AddressZero);
expect(admin).to.eq(signer.address);
expect(maxValidBlocknumber.toString()).to.eq(UINT32_MAX.toString());
expect(amountSpent.toNumber()).to.eq(0);
@@ -448,7 +448,7 @@ describe("Test chainlink:automationRegistry module", function () {
const {
target,
balance,
- lastAutomationNode,
+ lastKeeper,
admin,
maxValidBlocknumber,
amountSpent,
@@ -464,7 +464,7 @@ describe("Test chainlink:automationRegistry module", function () {
expect(target).to.eq(this.upkeep.address);
expect(balance.toNumber()).to.eq(0);
- expect(lastAutomationNode).to.eq(this.hre.ethers.constants.AddressZero);
+ expect(lastKeeper).to.eq(this.hre.ethers.constants.AddressZero);
expect(admin).to.eq(signer.address);
expect(maxValidBlocknumber.toString()).to.eq(UINT32_MAX.toString());
expect(amountSpent.toNumber()).to.eq(0);
@@ -716,7 +716,7 @@ describe("Test chainlink:automationRegistry module", function () {
const {
target,
balance,
- lastAutomationNode,
+ lastKeeper,
admin,
maxValidBlocknumber,
amountSpent,
@@ -732,7 +732,7 @@ describe("Test chainlink:automationRegistry module", function () {
expect(target).to.eq(this.upkeep.address);
expect(balance.toNumber()).to.eq(0);
- expect(lastAutomationNode).to.eq(this.hre.ethers.constants.AddressZero);
+ expect(lastKeeper).to.eq(this.hre.ethers.constants.AddressZero);
expect(admin).to.eq(signer.address);
expect(maxValidBlocknumber.toString()).to.eq(UINT32_MAX.toString());
expect(amountSpent.toNumber()).to.eq(0);
diff --git a/test/fixture-projects/hardhat-chainlink-arbitrum-goerli/hardhat.config.ts b/test/fixture-projects/hardhat-chainlink-arbitrum-goerli/hardhat.config.ts
index 9ddd3e6..b05dd58 100644
--- a/test/fixture-projects/hardhat-chainlink-arbitrum-goerli/hardhat.config.ts
+++ b/test/fixture-projects/hardhat-chainlink-arbitrum-goerli/hardhat.config.ts
@@ -7,7 +7,7 @@ const config: HardhatUserConfig = {
defaultNetwork: "arbitrum-goerli",
networks: {
"arbitrum-goerli": {
- url: "https://endpoints.omniatech.io/v1/arbitrum/goerli/public", // Public RPC node, skipping tests using it in Github Actions
+ url: "https://arbitrum-goerli.publicnode.com", // Public RPC node, skipping tests using it in Github Actions
accounts: [
"ffd78ff52b10e30f9d9b61dc7d0f44ceb5727b9383d28c9d19983a6df6c6ceaf", // Random PK
],
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/BlockhashStore.sol b/test/fixture-projects/hardhat-chainlink/contracts/BlockhashStore.sol
index e3f3b0b..d88b193 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/BlockhashStore.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/BlockhashStore.sol
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.6.6;
-import "@chainlink/contracts/src/v0.6/dev/BlockhashStore.sol";
+import "@chainlink/contracts/src/v0.6/BlockhashStore.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/FunctionsConsumer.sol b/test/fixture-projects/hardhat-chainlink/contracts/FunctionsConsumer.sol
new file mode 100644
index 0000000..7de963f
--- /dev/null
+++ b/test/fixture-projects/hardhat-chainlink/contracts/FunctionsConsumer.sol
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.19;
+
+import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/FunctionsClient.sol";
+import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
+import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/libraries/FunctionsRequest.sol";
+
+/**
+ * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
+ * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
+ * DO NOT USE THIS CODE IN PRODUCTION.
+ */
+
+contract FunctionsConsumer is FunctionsClient, ConfirmedOwner {
+ using FunctionsRequest for FunctionsRequest.Request;
+
+ bytes32 public donId; // DON ID for the Functions DON to which the requests are sent
+
+ bytes32 public s_lastRequestId;
+ bytes public s_lastResponse;
+ bytes public s_lastError;
+
+ constructor(address router, bytes32 _donId) FunctionsClient(router) ConfirmedOwner(msg.sender) {
+ donId = _donId;
+ }
+
+ /**
+ * @notice Set the DON ID
+ * @param newDonId New DON ID
+ */
+ function setDonId(bytes32 newDonId) external onlyOwner {
+ donId = newDonId;
+ }
+
+ /**
+ * @notice Triggers an on-demand Functions request
+ * @param source JavaScript source code
+ * @param secretsLocation Location of secrets (only Location.Remote & Location.DONHosted are supported)
+ * @param encryptedSecretsReference Reference pointing to encrypted secrets
+ * @param args String arguments passed into the source code and accessible via the global variable `args`
+ * @param bytesArgs Bytes arguments passed into the source code and accessible via the global variable `bytesArgs` as hex strings
+ * @param subscriptionId Subscription ID used to pay for request (FunctionsConsumer contract address must first be added to the subscription)
+ * @param callbackGasLimit Maximum amount of gas used to call the inherited `handleOracleFulfillment` method
+ */
+ function sendRequest(
+ string calldata source,
+ FunctionsRequest.Location secretsLocation,
+ bytes calldata encryptedSecretsReference,
+ string[] calldata args,
+ bytes[] calldata bytesArgs,
+ uint64 subscriptionId,
+ uint32 callbackGasLimit
+ ) external onlyOwner {
+ FunctionsRequest.Request memory req;
+ req.initializeRequest(FunctionsRequest.Location.Inline, FunctionsRequest.CodeLanguage.JavaScript, source);
+ req.secretsLocation = secretsLocation;
+ req.encryptedSecretsReference = encryptedSecretsReference;
+ if (args.length > 0) {
+ req.setArgs(args);
+ }
+ if (bytesArgs.length > 0) {
+ req.setBytesArgs(bytesArgs);
+ }
+ s_lastRequestId = _sendRequest(req.encodeCBOR(), subscriptionId, callbackGasLimit, donId);
+ }
+
+ /**
+ * @notice Triggers an on-demand Functions request that has been encoded off-chain
+ * @param encodedRequest CBOR-encoded Functions request
+ * @param subscriptionId Subscription ID used to pay for request (FunctionsConsumer contract address must first be added to the subscription)
+ * @param callbackGasLimit Maximum amount of gas used to call the inherited `handleOracleFulfillment` method
+ */
+ function sendEncodedRequest(
+ bytes calldata encodedRequest,
+ uint64 subscriptionId,
+ uint32 callbackGasLimit
+ ) external onlyOwner {
+ s_lastRequestId = _sendRequest(encodedRequest, subscriptionId, callbackGasLimit, donId);
+ }
+
+ /**
+ * @notice Store latest result/error
+ * @param requestId The request ID, returned by sendRequest()
+ * @param response Aggregated response from the user code
+ * @param err Aggregated error from the user code or from the execution pipeline
+ * Either response or error parameter will be set, but never both
+ */
+ function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override {
+ s_lastResponse = response;
+ s_lastError = err;
+ }
+}
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistrar.sol b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistrar.sol
index 48d2152..67486fc 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistrar.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistrar.sol
@@ -2,4 +2,4 @@
pragma solidity ^0.8.0;
-import "@chainlink/contracts/src/v0.8/KeeperRegistrar.sol";
+import "@chainlink/contracts/src/v0.8/automation/v1_2/KeeperRegistrar1_2.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistry1_3.sol b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistry1_3.sol
index 752589d..0293ce3 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistry1_3.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistry1_3.sol
@@ -2,4 +2,4 @@
pragma solidity ^0.8.0;
-import "@chainlink/contracts/src/v0.8/KeeperRegistry1_3.sol";
+import "@chainlink/contracts/src/v0.8/automation/v1_3/KeeperRegistry1_3.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistryLogic1_3.sol b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistryLogic1_3.sol
index 7614631..6af9359 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistryLogic1_3.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/KeeperRegistryLogic1_3.sol
@@ -2,4 +2,4 @@
pragma solidity ^0.8.0;
-import "@chainlink/contracts/src/v0.8/KeeperRegistryLogic1_3.sol";
+import "@chainlink/contracts/src/v0.8/automation/v1_3/KeeperRegistryLogic1_3.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/UpkeepTranscoder.sol b/test/fixture-projects/hardhat-chainlink/contracts/UpkeepTranscoder.sol
index af887dd..043e84b 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/UpkeepTranscoder.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/UpkeepTranscoder.sol
@@ -2,4 +2,4 @@
pragma solidity ^0.8.0;
-import "@chainlink/contracts/src/v0.8/UpkeepTranscoder.sol";
+import "@chainlink/contracts/src/v0.8/automation/UpkeepTranscoder.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/contracts/VRFCoordinatorV2.sol b/test/fixture-projects/hardhat-chainlink/contracts/VRFCoordinatorV2.sol
index bb2d714..f60e10f 100644
--- a/test/fixture-projects/hardhat-chainlink/contracts/VRFCoordinatorV2.sol
+++ b/test/fixture-projects/hardhat-chainlink/contracts/VRFCoordinatorV2.sol
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
-import "@chainlink/contracts/src/v0.8/VRFCoordinatorV2.sol";
+import "@chainlink/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol";
diff --git a/test/fixture-projects/hardhat-chainlink/hardhat.config.ts b/test/fixture-projects/hardhat-chainlink/hardhat.config.ts
index 69d4149..a9dd89d 100644
--- a/test/fixture-projects/hardhat-chainlink/hardhat.config.ts
+++ b/test/fixture-projects/hardhat-chainlink/hardhat.config.ts
@@ -14,7 +14,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
- version: "0.8.18",
+ version: "0.8.19",
settings: commonCompilerSettings,
},
{
diff --git a/test/fixture-projects/hardhat/hardhat.config.ts b/test/fixture-projects/hardhat/hardhat.config.ts
new file mode 100644
index 0000000..d6cd2ef
--- /dev/null
+++ b/test/fixture-projects/hardhat/hardhat.config.ts
@@ -0,0 +1,10 @@
+import "@nomiclabs/hardhat-ethers";
+import { HardhatUserConfig } from "hardhat/types";
+
+import "../../../src/index";
+
+const config: HardhatUserConfig = {
+ chainlink: {},
+};
+
+export default config;
diff --git a/test/functionsRouter.test.ts b/test/functionsRouter.test.ts
new file mode 100644
index 0000000..282e7fa
--- /dev/null
+++ b/test/functionsRouter.test.ts
@@ -0,0 +1,614 @@
+import { deployFunctionsOracle } from "@chainlink/functions-toolkit";
+import { expect } from "chai";
+import { BigNumber, Contract, Wallet } from "ethers";
+
+import { PACKAGE_NAME } from "../src/shared/constants";
+import { FunctionsSubtask, Task } from "../src/shared/enums";
+
+import { useEnvironment } from "./helpers";
+
+describe("Test chainlink:functionsRouter module", function () {
+ const LINK_AMOUNT_TO_TRANSFER = "1000000000000000000";
+ const LINK_AMOUNT_TO_FUND = "1000000000000";
+ const ETH_AMOUNT_TO_FUND = "100";
+ const CALLBACK_GAS_LIMIT = 300_000;
+ const GAS_PRICE_WEI = BigNumber.from("100000000000");
+ const ESTIMATED_COST_JUELS = BigInt("38923000000000000000");
+
+ let wallet: Wallet;
+ let donId: string;
+ let linkTokenContract: Contract;
+ let functionsRouterContract: Contract;
+ let functionsConsumerContract: Contract;
+
+ function beforeShared() {
+ return async function (this: Mocha.Context) {
+ const [account] = await this.hre.ethers.getSigners();
+ const amountInWei = this.hre.ethers.utils.parseEther(ETH_AMOUNT_TO_FUND);
+
+ wallet = new this.hre.ethers.Wallet(
+ this.hre.ethers.Wallet.createRandom().privateKey,
+ this.hre.ethers.provider
+ );
+ await account.sendTransaction({
+ to: wallet.address,
+ value: amountInWei,
+ });
+ const deployment = await deployFunctionsOracle(wallet);
+ donId = deployment.donId;
+ linkTokenContract = deployment.linkTokenContract;
+ functionsRouterContract = deployment.functionsRouterContract;
+ functionsConsumerContract = await (
+ await this.hre.ethers.getContractFactory("FunctionsConsumer")
+ )
+ .connect(account)
+ .deploy(
+ functionsRouterContract.address,
+ this.hre.ethers.utils.formatBytes32String(donId)
+ );
+
+ await linkTokenContract
+ .connect(wallet)
+ .transfer(account.address, LINK_AMOUNT_TO_TRANSFER);
+ };
+ }
+
+ describe("Run methods as hre methods", function () {
+ useEnvironment("hardhat-chainlink");
+
+ before(beforeShared());
+
+ it("Create Subscription", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+
+ expect(subscriptionId.toNumber()).gt(0);
+ });
+
+ it("Fund Subscription", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.fundSubscription(
+ functionsRouterContract.address,
+ linkTokenContract.address,
+ LINK_AMOUNT_TO_FUND,
+ subscriptionId
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.balance.toNumber()).gte(10000000);
+ });
+
+ it("Cancel Subscription", async function () {
+ const [account] = await this.hre.ethers.getSigners();
+ const balanceInitial = await linkTokenContract.balanceOf(account.address);
+
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.fundSubscription(
+ functionsRouterContract.address,
+ linkTokenContract.address,
+ LINK_AMOUNT_TO_FUND,
+ subscriptionId
+ );
+ await this.hre.chainlink.functions.cancelSubscription(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ const balance = await linkTokenContract.balanceOf(account.address);
+ expect(balance.toString()).eq(balanceInitial.toString());
+ });
+
+ it("Add Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.addConsumer(
+ functionsRouterContract.address,
+ functionsConsumerContract.address,
+ subscriptionId
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.true;
+ });
+
+ it("Remove Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.addConsumer(
+ functionsRouterContract.address,
+ functionsConsumerContract.address,
+ subscriptionId
+ );
+ await this.hre.chainlink.functions.removeConsumer(
+ functionsRouterContract.address,
+ functionsConsumerContract.address,
+ subscriptionId
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.false;
+ });
+
+ it("Transfer Subscription Ownership", async function () {
+ const [_, receiver] = await this.hre.ethers.getSigners();
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.requestSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ receiver.address
+ );
+ await this.hre.chainlink.functions.acceptSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ {
+ signer: receiver,
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.owner).eq(receiver.address);
+ });
+
+ it("Timeout Requests", async function () {
+ try {
+ await this.hre.chainlink.functions.timeoutRequests(
+ functionsRouterContract.address,
+ []
+ );
+ } catch (err: any) {
+ expect(err.message).to.eq(
+ "Must provide at least one request commitment"
+ );
+ }
+ });
+
+ it("Estimate Request Cost", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ const estimatedCost =
+ await this.hre.chainlink.functions.estimateRequestCost(
+ functionsRouterContract.address,
+ donId,
+ subscriptionId,
+ CALLBACK_GAS_LIMIT,
+ GAS_PRICE_WEI
+ );
+
+ expect(estimatedCost.toString()).eq(ESTIMATED_COST_JUELS.toString());
+ });
+ });
+
+ describe("Run methods as hre subtasks", function () {
+ useEnvironment("hardhat-chainlink");
+
+ before(beforeShared());
+
+ it("Create Subscription", async function () {
+ const { subscriptionId } = await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.createSubscription}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ }
+ );
+
+ expect(subscriptionId.toNumber()).gt(0);
+ });
+
+ it("Fund Subscription", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.fundSubscription}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ linkTokenAddress: linkTokenContract.address,
+ amountInJuels: LINK_AMOUNT_TO_FUND,
+ subscriptionId: subscriptionId.toString(),
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.balance.toNumber()).gte(10000000);
+ });
+
+ it("Cancel Subscription", async function () {
+ const [account] = await this.hre.ethers.getSigners();
+ const balanceInitial = await linkTokenContract.balanceOf(account.address);
+
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.fundSubscription(
+ functionsRouterContract.address,
+ linkTokenContract.address,
+ LINK_AMOUNT_TO_FUND,
+ subscriptionId
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.cancelSubscription}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }
+ );
+
+ const balance = await linkTokenContract.balanceOf(account.address);
+ expect(balance.toString()).eq(balanceInitial.toString());
+ });
+
+ it("Add Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.addConsumer}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ consumerAddress: functionsConsumerContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.true;
+ });
+
+ it("Remove Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.addConsumer(
+ functionsRouterContract.address,
+ functionsConsumerContract.address,
+ subscriptionId
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.removeConsumer}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ consumerAddress: functionsConsumerContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.false;
+ });
+
+ it("Request Subscription Ownership Transfer", async function () {
+ const [_, receiver] = await this.hre.ethers.getSigners();
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.requestSubscriptionOwnerTransfer}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ newOwnerAddress: receiver.address,
+ }
+ );
+ await this.hre.chainlink.functions.acceptSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ {
+ signer: receiver,
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.owner).eq(receiver.address);
+ });
+
+ it("Accept Subscription Ownership Transfer", async function () {
+ const [receiver, owner] = await this.hre.ethers.getSigners();
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address,
+ "",
+ {
+ signer: owner,
+ }
+ );
+ await this.hre.chainlink.functions.requestSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ receiver.address,
+ {
+ signer: owner,
+ }
+ );
+ await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}:${FunctionsSubtask.acceptSubscriptionOwnerTransfer}`,
+ {
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.owner).eq(receiver.address);
+ });
+ });
+
+ describe("Run methods as subtasks of a hre task", function () {
+ useEnvironment("hardhat-chainlink");
+
+ before(beforeShared());
+
+ it("Create Subscription", async function () {
+ const { subscriptionId } = await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functions}`,
+ {
+ subtask: FunctionsSubtask.createSubscription,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ consumerAddress: "",
+ }),
+ }
+ );
+
+ expect(subscriptionId.toNumber()).gt(0);
+ });
+
+ it("Fund Subscription", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.fundSubscription,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ linkTokenAddress: linkTokenContract.address,
+ amountInJuels: LINK_AMOUNT_TO_FUND,
+ subscriptionId: subscriptionId.toString(),
+ }),
+ });
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.balance.toNumber()).gte(10000000);
+ });
+
+ it("Cancel Subscription", async function () {
+ const [account] = await this.hre.ethers.getSigners();
+ const balanceInitial = await linkTokenContract.balanceOf(account.address);
+
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.fundSubscription(
+ functionsRouterContract.address,
+ linkTokenContract.address,
+ LINK_AMOUNT_TO_FUND,
+ subscriptionId
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.cancelSubscription,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ receivingAddress: "",
+ }),
+ });
+
+ const balance = await linkTokenContract.balanceOf(account.address);
+ expect(balance.toString()).eq(balanceInitial.toString());
+ });
+
+ it("Add Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.addConsumer,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ consumerAddress: functionsConsumerContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }),
+ });
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.true;
+ });
+
+ it("Remove Subscription Consumer", async function () {
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.chainlink.functions.addConsumer(
+ functionsRouterContract.address,
+ functionsConsumerContract.address,
+ subscriptionId
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.removeConsumer,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ consumerAddress: functionsConsumerContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }),
+ });
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(
+ subscriptionDetails.consumers.includes(
+ functionsConsumerContract.address
+ )
+ ).to.be.false;
+ });
+
+ it("Request Subscription Ownership Transfer", async function () {
+ const [_, receiver] = await this.hre.ethers.getSigners();
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.requestSubscriptionOwnerTransfer,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ newOwnerAddress: receiver.address,
+ }),
+ });
+ await this.hre.chainlink.functions.acceptSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ {
+ signer: receiver,
+ }
+ );
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.owner).eq(receiver.address);
+ });
+
+ it("Accept Subscription Ownership Transfer", async function () {
+ const [receiver, owner] = await this.hre.ethers.getSigners();
+ const { subscriptionId } =
+ await this.hre.chainlink.functions.createSubscription(
+ functionsRouterContract.address,
+ "",
+ {
+ signer: owner,
+ }
+ );
+ await this.hre.chainlink.functions.requestSubscriptionOwnerTransfer(
+ functionsRouterContract.address,
+ subscriptionId,
+ receiver.address,
+ {
+ signer: owner,
+ }
+ );
+ await this.hre.run(`${PACKAGE_NAME}:${Task.functions}`, {
+ subtask: FunctionsSubtask.acceptSubscriptionOwnerTransfer,
+ args: JSON.stringify({
+ functionsRouterAddress: functionsRouterContract.address,
+ subscriptionId: subscriptionId.toString(),
+ }),
+ });
+
+ const subscriptionDetails =
+ await this.hre.chainlink.functions.getSubscriptionDetails(
+ functionsRouterContract.address,
+ subscriptionId
+ );
+
+ expect(subscriptionDetails.owner).eq(receiver.address);
+ });
+ });
+});
diff --git a/test/functionsSimulation.test.ts b/test/functionsSimulation.test.ts
new file mode 100644
index 0000000..d291d29
--- /dev/null
+++ b/test/functionsSimulation.test.ts
@@ -0,0 +1,78 @@
+import { expect } from "chai";
+
+import { PACKAGE_NAME } from "../src/shared/constants";
+import { FunctionsSimulationSubtask, Task } from "../src/shared/enums";
+
+import { useEnvironment } from "./helpers";
+
+const isGithubActions = !!process.env.GITHUB_ACTIONS;
+
+describe("Test chainlink:sandbox:functionsSimulation module [SKIP FOR GITHUB ACTIONS]", function () {
+ useEnvironment("hardhat");
+
+ describe("Run methods as hre methods", function () {
+ it("Run functions simulation", async function () {
+ if (isGithubActions) this.skip();
+
+ const result =
+ await this.hre.chainlink.sandbox.functionsSimulation.simulateRequest(
+ 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])',
+ { test: "hello" },
+ ["hello", "world"],
+ ["0x1234", "0x5678"]
+ );
+
+ expect(result.capturedTerminalOutput).to.eq("");
+ expect(result.responseBytesHexstring).to.eq(
+ "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738"
+ );
+ });
+ });
+
+ describe("Run methods as hre subtasks", function () {
+ it("Run functions simulation", async function () {
+ if (isGithubActions) this.skip();
+
+ const resultJSON = await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functionsSimulation}:${FunctionsSimulationSubtask.simulateRequest}`,
+ {
+ source:
+ 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])',
+ secrets: '{"test":"hello"}',
+ args: "hello, world",
+ bytesArgs: "0x1234, 0x5678",
+ }
+ );
+ const result = JSON.parse(resultJSON);
+ expect(result.capturedTerminalOutput).to.eq("");
+ expect(result.responseBytesHexstring).to.eq(
+ "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738"
+ );
+ });
+ });
+
+ describe("Run methods as subtasks of a hre task", function () {
+ it("Run functions simulation", async function () {
+ if (isGithubActions) this.skip();
+
+ const resultJSON = await this.hre.run(
+ `${PACKAGE_NAME}:${Task.functionsSimulation}`,
+ {
+ subtask: FunctionsSimulationSubtask.simulateRequest,
+ args: JSON.stringify({
+ source:
+ 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])',
+ secrets: '{"test":"hello"}',
+ args: "hello, world",
+ bytesArgs: "0x1234, 0x5678",
+ }),
+ }
+ );
+ const result = JSON.parse(resultJSON);
+ expect(result.capturedTerminalOutput).to.eq("");
+ expect(result.responseBytesHexstring).to.eq(
+ "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738"
+ );
+ });
+ });
+});