NEW

Join us at SmartCon 2023—where Web3 gets real. Get your ticket.

Adding Chainlink Functions to an Existing Project

The Chainlink Functions Starter Kit lets you run several example requests, but extra steps are required to add Chainlink Functions to your existing projects.

If you are new to Chainlink Functions, complete the steps in the Getting Started Guide to learn the basics. If you already have a project, you can skip to the Libraries and Dependencies section. You can also use the Chainlink Functions Playground to simulate Chainlink Functions within the browser, call APIs, and execute demo requests.

Using Chainlink Functions to your existing projects requires the following components:

  • A consumer contract: Use this contract to send requests to the Chainlink Functions decentralized oracle network (DON). The consumer contract imports the following dependencies:
  • A Chainlink Functions subscription: The subscription is used to pay for Chainlink Functions requests when they are fulfilled. You can create and fund subscriptions using the starter kit tools, but this guide shows you how to manage subscriptions programmatically.

Before you begin

This guide assumes you are using a Hardhat JavaScript project with Node.js, but you can modify it to work with other frameworks. If you already have a project, you can skip to the Libraries and Dependencies section.

You must have a wallet with testnet LINK and native gas tokens to run this example. This example uses Polygon Mumbai, but you can use any of the Supported Networks. You can get testnet LINK at faucets.chain.link and testnet MATIC at the Polygon Faucet. To learn how to get testnet funds for other networks, see the LINK Token Contracts page.

Configure Hardhat

If you don't already have a Hardhat project, start one with the following steps:

  1. Install Node.js. Note: Node.js version 18 is required.

  2. Make a new project directory:

    mkdir new-functions-project && cd new-functions-project
  3. Create a new Node.js project:

    npm init
  4. Install Hardhat as a dev dependency:

    npm install --save-dev hardhat
  5. Install the dependency packages:

    npm install @chainlink/contracts @chainlink/env-enc @openzeppelin/contracts @openzeppelin/contracts-upgradeable eth-crypto dotenv axios vm2 is-http-url prompt-sync
  6. Create a new Hardhat project. For this example, create a JavaScript project and use the default configurations:

    npx hardhat
  7. Remove the Lock.sol contract that Hardhat creates by default. This contract is not necessary to compile it for this tutorial.

    rm ./contracts/Lock.sol
  8. Use curl to get the example hardhat config file hardhat.config.js:

    curl -o hardhat.config.js https://raw.githubusercontent.com/smartcontractkit/smart-contract-examples/main/add-functions-to-project/hardhat.config.js

    Alternatively, you can copy and paste this file from the smartcontractkit/smart-contract-examples repository.

  9. Open hardhat.config.js. To use a different network, see the Supported Networks page for a list of networks where Chainlink Functions DONs are available. This example uses Polygon Mumbai by default.

  10. For higher security, you should encrypt your environment variables at rest.

    1. Set an encryption password for your environment variables.

      npx env-enc set-pw
    2. Run npx env-enc set to configure a .env.enc file with the basic variables that you need to send your requests to the Polygon Mumbai network.

      • POLYGON_MUMBAI_RPC_URL: Set a URL for the Polygon Mumbai testnet. You can sign up for a personal endpoint from Alchemy, Infura, or another node provider service.

      • PRIVATE_KEY: Find the private key for your testnet wallet. If you use MetaMask, follow the instructions to Export a Private Key. Set this in the .env file. Note: The Chainlink Functions hardhat starter kit uses your private key to sign any transactions you make such as deploying your consumer contract, creating subscriptions, and making requests.

      npx env-enc set

Run the compile command for Hardhat or your framework of choice to make sure everything is configured correctly. You will see several compile warnings, but everything is correct if the Solidity files compile successfully. For this example, run:

npx hardhat compile

Get the dependency contracts and scripts

Get the required libraries from the Chainlink Functions Starter Kit. You can clone the repo and copy the folders into your existing project manually, or use a single command to get a tarball through the GitHub API and extract the folders you need to the correct location in your project.

  1. Open a terminal and change directories to the root of your project. Usually this is the folder with package.json.

  2. Run the curl command to download the latest tarball from the starter kit main branch and then run tar -xf with the --strip flag to extract only the files you need. The following curl command combines both steps:

    curl -L -o ../functions.tar.gz https://api.github.com/repos/smartcontractkit/functions-hardhat-starter-kit/tarball/main &&
    tar -xf ../functions.tar.gz --strip=1 --wildcards smartcontractkit-functions-hardhat-starter-kit-*/contracts/dev smartcontractkit-functions-hardhat-starter-kit-*/contracts/test smartcontractkit-functions-hardhat-starter-kit-*/FunctionsSandboxLibrary smartcontractkit-functions-hardhat-starter-kit-*/contracts/FunctionsConsumer.sol

    Alternatively, you can download the tarball directly from GitHub and extract the dependencies manually. This tarball comes from the smartcontractkit/functions-hardhat-starter-kit repository, which is different from the smartcontractkit/smart-contract-examples repository even if it contains some similar files.

When you are done, you should have the necessary dependencies in the following directories:

  • contracts/dev
  • contracts/test
  • FunctionsSandboxLibrary

Run the compile command to make sure all the dependencies are satisfied. Add missing dependencies or Hardhat configuration options as necessary. For Hardhat, run npx hardhat compile.

npx hardhat compile

Configure on-chain resources

The on-chain resources are critical for Chainlink Functions to process your requests.

  1. Create a consumer contract
  2. Deploy a consumer contract

Create a consumer contract

Use the FunctionsConsumer.sol, which is already in your ./contracts folder. You can modify it to fit your needs and redeploy it later. Optionally, you can take an existing contract of your own and enable it to handle Chainlink Functions requests. Just make sure that it meets the requirements listed in this guide.

In general, a consumer contract requires several components:

  • To write a Chainlink Functions consumer contract, your contract must import FunctionsClient.sol. You can read the API reference: FunctionsClient.

    This contract is not available in an NPM package, so you must download and import it from within your project.

    import {Functions, FunctionsClient} from "./dev/functions/FunctionsClient.sol";
  • Use the Functions.sol library to get all the functions needed for building a Chainlink Functions request. You can read the API reference: Functions.

    using Functions for Functions.Request;
    
  • The latest request id, latest received response, and latest received error (if any) are defined as state variables. Note latestResponse and latestError are encoded as dynamically sized byte array bytes, so you will still need to decode them to read the response or error:

    bytes32 public latestRequestId;
    bytes public latestResponse;
    bytes public latestError;
  • We define the OCRResponse event that your smart contract will emit during the callback

    event OCRResponse(bytes32 indexed requestId, bytes result, bytes err);
  • Pass the oracle address for your network when you deploy the contract:

    constructor(address oracle) FunctionsClient(oracle)
  • At any time, you can change the oracle address by calling the updateOracleAddress function.

  • The two remaining functions are:

    • executeRequest for sending a request. It receives the JavaScript source code, encrypted secrets, list of arguments to pass to the source code, subscription id, and callback gas limit as parameters. Then:

      • It uses the Functionslibrary to initialize the request and add any passed encrypted secrets or arguments. You can read the API Reference for Initializing a request, adding secrets, and adding arguments.

        Functions.Request memory req;
        req.initializeRequest(Functions.Location.Inline, Functions.CodeLanguage.JavaScript, source);
        if (secrets.length > 0) {
          req.addRemoteSecrets(secrets);
        }
        if (args.length > 0) req.addArgs(args);
      • It sends the request to the oracle by calling the FunctionsClient sendRequest function. You can read the API reference for sending a request. Finally, it stores the request id in latestRequestId.

        bytes32 assignedReqID = sendRequest(req, subscriptionId, gasLimit);
        latestRequestId = assignedReqID;
    • fulfillRequest to be invoked during the callback. This function is defined in FunctionsClient as virtual (read fulfillRequest API reference). So, your smart contract must override the function to implement the callback. The implementation of the callback is straightforward: the contract stores the latest response and error in latestResponse and latestError before emitting the OCRResponse event.

      latestResponse = response;
      latestError = err;
      emit OCRResponse(requestId, response, err);

Next, deploy the contract.

Deploy a consumer contract

You can deploy consumer contracts using the Chainlink Functions Starter Kit, but this example shows how to deploy the contract programmatically using a script.

  1. Use curl to get the example deployment script deploy.js and put it in the ./scripts folder of your project:

    curl -o ./scripts/deploy.js https://raw.githubusercontent.com/smartcontractkit/smart-contract-examples/main/add-functions-to-project/scripts/deploy.js

    Alternatively, you can copy and paste the script from the smartcontractkit/smart-contract-examples repository.

  2. Open deploy.js in your editor of choice.

  3. Set the oracleAddress to the oracle address on the network that you want to use. Each network has a unique DON with a unique oracle address. See the Supported Networks page for a list of supported networks and oracle addresses. For this example, use the address for the oracle on Polygon Mumbai:

    const oracleAddress = "0xeA6721aC65BCeD841B8ec3fc5fEdeA6141a0aDE4"
  4. Set const contractName to the name of the contract that you want Hardhat to deploy. The ethers.getContractFactory(contractName); line creates a ContractFactory object using the contract that you define. For this example, use FunctionsConsumer.

    const contractName = "FunctionsConsumer"const consumerContract = await ethers.getContractFactory(contractName);
  5. Save the file and run the script to deploy your contract. Include the --network flag to use a network other than the default in the Hardhat config:

    npx hardhat run ./scripts/deploy.js --network your_network

    Example:

    npx hardhat run ./scripts/deploy.js --network polygonMumbai
  6. If your contract deployed successfully, you will see the deployed consumer address. Record this address to use later:

    Deployed Functions Consumer address: 0x5484e266c2cD379800e6F27EaB097Bb806647CbF

Next, create and fund your Chainlink Functions subscription.

Create and fund a subscription

You can use the Chainlink Functions Starter Kit to create and manage your subscriptions. See Managing Subscriptions for instructions.

This example shows how to create and manage subscriptions programmatically. You can create the subscription, fund the subscription, and authorize the consumer all in one script. If you have not already signed up for limited Beta access to Chainlink Functions, apply here to add your EVM account address to the Allow List.

  1. Ensure that the wallet address you are using to create the subscription has a sufficient LINK balance. You can get testnet LINK at faucets.chain.link. To get testnet funds for other networks, see the LINK Token Contracts page.

  2. Use curl to get the example subscription script functions-sub.js and put it in the ./scripts folder of your project:

    curl -o ./scripts/functions-sub.js https://raw.githubusercontent.com/smartcontractkit/smart-contract-examples/main/add-functions-to-project/scripts/functions-sub.js

    Alternatively, you can copy and paste this script from the smartcontractkit/smart-contract-examples repository.

  3. Open functions-sub.js in your editor of choice.

  4. Set const linkAmount with the amount of LINK you want to send to the subscription. You can retrieve extra funds later when you cancel the subscription.

    const linkAmount = "1"
  5. Set const consumer to the address of the consumer contract that you deployed:

    const consumer = "0x5484e266c2cD379800e6F27EaB097Bb806647CbF"
  6. Save the file and run the script. Include the --network flag to use a network other than the default in the Hardhat config:

    npx hardhat run scripts/functions-sub.js --network your_network

    Example:

    npx hardhat run scripts/functions-sub.js --network polygonMumbai
  7. If the script is successful, you the terminal prints your subscription ID. Record this ID to use for Chainlink Functions requests. You should see output similar to the following example:

    Subscription created with ID: 917
    Duplicate definition of Transfer (Transfer(address,address,uint256,bytes), Transfer(address,address,uint256))
    Funding with 1000000000000000000 Juels (1 LINK = 10^18 Juels)
    Subscription 917 funded with 1000000000000000000 Juels (1 LINK = 10^18 Juels)
    Adding consumer contract address 0x5484e266c2cD379800e6F27EaB097Bb806647CbF to subscription 917
    Authorized consumer contract: 0x5484e266c2cD379800e6F27EaB097Bb806647CbF

Now that the consumer contract is deployed and the subscription is created and funded with LINK, the on-chain resources are ready to handle your requests. Next, create JavaScript code that you want to run on the DON, configure arguments for the code, and create a script to send your request.

Send requests

After your on-chain resources are configured, you can send Chainlink Functions requests to the DON. This can be done from a Web3 application, script, another on-chain smart contract, or any other location capable of submitting requests to your consumer contract. This example shows you how to generate a request, encrypt secrets, send your request, and read the fulfillment response.

Each request has the following components:

  • Source code: JavaScript code that will run on the DON.
  • Arguments: Optional arguments for the source code. The arguments that you need are defined in your source code. Depending on how you configured your source, you might not need any arguments at all.
  • Secrets: Optional secrets that your source code needs to access APIs or other interfaces. See the Using Secrets in Requests and Using Off-chain Secrets tutorials for examples.

Create a request script

If you already have source code or want to write your own source code, put it in a file in your project. Later, you can specify the path to this file before you submit your request.

For this example, download some example source code and create a script to assemble the required components. The script will read your source code, define arguments, encrypt secrets, and send requests to your consumer contract. This example script does not require Hardhat, so you can modify it to run in a browser using Ethers or another framework.

  1. Use curl to get the example source code. This code runs on each node in the DON and returns a response to your consumer contract. For this example, use the source from the Call an API tutorial. The following curl request creates a file named Functions-request-source.js with the source code:

    curl -o Functions-request-source.js https://raw.githubusercontent.com/smartcontractkit/smart-contract-examples/main/add-functions-to-project/Functions-request-source.js

    Alternatively, you can copy and paste this source code file from the smartcontractkit/smart-contract-examples repository.

  2. Get the example script and put it in the ./scripts directory:

    curl -o ./scripts/request.js https://raw.githubusercontent.com/smartcontractkit/smart-contract-examples/main/add-functions-to-project/scripts/request.js

    Alternatively, you can copy and paste the script from the smartcontractkit/smart-contract-examples repository.

  3. Edit the request.js script and set your deployed consumer address in const consumerAddress:

    const consumerAddress = 0x5484e266c2cD379800e6F27EaB097Bb806647CbF
  4. Set const subscriptionId to your Chainlink Functions subscription ID. This must be the same ID that your consumer contract is authorized to use. You can add a consumer contract to a subscription later if you need to.

    const subscriptionId = 917
  5. Set const consumerAbiPath to the ABI file that you created when you ran npx hardhat compile. The ABI tells Ethers how to interact with your deployed contract. If you compiled and deployed FunctionsConsumer.sol, set a path like the following example:

    const consumerAbiPath = "./artifacts/contracts/FunctionsConsumer.sol/FunctionsConsumer.json"
  6. Set const source with the path to the request source code that you downloaded earlier:

    const source = ./Functions-request-source.js
  7. Set const args with an array of arguments for the DON to use when it runs your source. If you look at the Functions-request-source.js file, you can see that it requires two arguments. The args define which assets to retrieve from the data source URL.

    const args = ["ETH", "USD"]
  8. Set const oracleAddress to the oracle address on the network that you want to use. Each network has a unique DON with a unique oracle address. See the Supported Networks page for a list of supported networks and oracle addresses. For this example, use the address for the oracle on Polygon Mumbai:

    const oracleAddress = "0xeA6721aC65BCeD841B8ec3fc5fEdeA6141a0aDE4"
  9. Set const oracleAbiPath to the ABI for the oracle contract. If you downloaded and compiled the dependencies for deploying your consumer contract, the oracle ABI is already generated in ./artifacts/contracts/dev/functions/:

    const oracleAbiPath = "./artifacts/contracts/dev/functions/FunctionsOracle.sol/FunctionsOracle.json"
  10. Secrets are not required for this example.

  11. Save and close the script.

  12. Run the script to send the request to the DON.

    npx hardhat run scripts/request.js --network your_network

    Example:

    npx hardhat run scripts/request.js --network polygonMumbai

If the script runs successfully, the script reads your consumer contract and prints the stored value that the DON returned.

Waiting 2 blocks for transaction 0x949184e7b605f034ce1d1d954973dc7132efb815bfa4861c54774cee457566d8 to be confirmed...

Request 0xb3a4f9f47caeb83c5125ee61e2db1ca41beaa8a6781fb3c4364b4d394c40bc6b initiated
Waiting for fulfillment...


Request 0xb3a4f9f47caeb83c5125ee61e2db1ca41beaa8a6781fb3c4364b4d394c40bc6b fulfilled!
Response returned to client contract represented as a hex string: 180972

Now you have the tools you need to build your own applications that use Chainlink Functions. Modify Functions-request-source.js and your input arguments to try out different capabilities. For more examples, see the Tutorials section.

What's next

Stay updated on the latest Chainlink news