NEW

The Constellation Hackathon is on: Compete for $500K+ in prizes.

Back

Getting Started

Learn how to make requests to the Chainlink Functions Decentralized Oracle Network (DON) and make any computation or API calls off-chain. Chainlink Functions is available on several blockchains (see the supported networks page), but this guide uses Polygon Mumbai to simplify access to testnet funds. Complete the following tasks to get started with Chainlink Functions:

  • Set up your web3 wallet and fund it with testnet tokens.
  • Simulate a Chainlink Functions on the Chainlink Functions Playground.
  • Send a Chainlink Functions request to the DON. The JavaScript source code makes an API call to the Star Wars API and fetches the name of a given character.
  • Receive the response from Chainlink Functions and parse the result.

Simulation

Before making a Chainlink Functions request from your smart contract, it is always a good practice to simulate the source code off-chain to make any adjustments or corrections.

  1. Open the Functions playground.

  2. Copy and paste the following source code into the playground's code block.

    const characterId = args[0];
    const apiResponse = await Functions.makeHttpRequest({
      url: `https://swapi.dev/api/people/${characterId}/`,
    });
    if (apiResponse.error) {
      throw Error("Request failed");
    }
    const { data } = apiResponse;
    return Functions.encodeString(data.name);
    
  3. Under Argument, set the first argument to 1. You are going to fetch the name of the first Star Wars character.

  4. Click on Run code. Under Output, you should see Luke Skywalker.

Configure your resources

Configure your wallet

You will test on Polygon Mumbai, so you must have an Ethereum web3 wallet with enough MATIC and LINK tokens. MATIC is the native gas fee token on Polygon. You will use MATIC tokens to pay for gas whenever you make a transaction on Polygon Mumbai. On the other hand, you will use LINK tokens to pay the Chainlink Functions Decentralized Oracles Network (DON) for processing your request.

  1. Install the MetaMask wallet or other Ethereum web3 wallet.

  2. Set the network for your wallet to the Polygon Mumbai testnet. If you need to add Mumbai to your wallet, you can find the chain ID and the LINK token contract address on the LINK Token Contracts page.

  3. Request testnet MATIC from the Polygon Faucet.

  4. Request testnet LINK from faucets.chain.link/mumbai.

Deploy a Functions consumer contract on Polygon Mumbai

  1. Open the GettingStartedFunctionsConsumer.sol contract in Remix.

  2. Compile the contract.

  3. Open MetaMask and select the Polygon Mumbai network.

  4. In Remix under the Deploy & Run Transactions tab, select Injected Provider - MetaMask in the Environment list. Remix will use the MetaMask wallet to communicate with Polygon Mumbai.

  5. Click the Deploy button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to make sure you are deploying the contract to Polygon Mumbai.

  6. After you confirm the transaction, the contract address appears in the Deployed Contracts list. Copy the contract address.

Create a subscription

Follow the Managing Functions Subscriptions guide to accept the Chainlink Functions Terms of Service (ToS), create a subscription, fund it, then add your consumer contract address to it.

You can find the Chainlink Functions Subscription Manager at functions.chain.link.

Run the example

The example is hardcoded to communicate with Chainlink Functions on Polygon Mumbai. Read the Examine the code section for a detailed description of all components.

To run the example:

  1. In Remix under the Deploy & Run Transactions tab, expand your contract in the Deployed Contracts section.

  2. Expand the sendRequest function to display its parameters.

  3. Fill in the subscriptionId with your subscription ID and args with [1], then click transact button.

  4. Wait for the request to be fulfilled. You can monitor the status of your request on the Chainlink Functions Subscription Manager.

  5. Once the status is Success, check the character name: In Remix, under the Deploy & Run Transactions tab, click on the character function, and you'll get the name of your character.

Chainlink Functions is capable of much more than just computation. Try one of the Tutorials to see examples that can GET and POST to public APIs, securely handle API secrets, handle custom responses, and query multiple APIs.

Examine the code

Solidity code

// 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";

/**
 * Request testnet LINK and ETH here: https://faucets.chain.link/
 * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/resources/link-token-contracts/
 */

/**
 * @title GettingStartedFunctionsConsumer
 * @notice This is an example contract to show how to make HTTP requests using Chainlink
 * @dev This contract uses hardcoded values and should not be used in production.
 */
contract GettingStartedFunctionsConsumer is FunctionsClient, ConfirmedOwner {
    using FunctionsRequest for FunctionsRequest.Request;

    // State variables to store the last request ID, response, and error
    bytes32 public s_lastRequestId;
    bytes public s_lastResponse;
    bytes public s_lastError;

    // Custom error type
    error UnexpectedRequestID(bytes32 requestId);

    // Event to log responses
    event Response(
        bytes32 indexed requestId,
        string character,
        bytes response,
        bytes err
    );

    // Router address - Hardcoded for Mumbai
    // Check to get the router address for your supported network https://docs.chain.link/chainlink-functions/supported-networks
    address router = 0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C;

    // JavaScript source code
    // Fetch character name from the Star Wars API.
    // Documentation: https://swapi.dev/documentation#people
    string source =
        "const characterId = args[0];"
        "const apiResponse = await Functions.makeHttpRequest({"
        "url: `https://swapi.dev/api/people/${characterId}/`"
        "});"
        "if (apiResponse.error) {"
        "throw Error('Request failed');"
        "}"
        "const { data } = apiResponse;"
        "return Functions.encodeString(data.name);";

    //Callback gas limit
    uint32 gasLimit = 300000;

    // donID - Hardcoded for Mumbai
    // Check to get the donID for your supported network https://docs.chain.link/chainlink-functions/supported-networks
    bytes32 donID =
        0x66756e2d706f6c79676f6e2d6d756d6261692d31000000000000000000000000;

    // State variable to store the returned character information
    string public character;

    /**
     * @notice Initializes the contract with the Chainlink router address and sets the contract owner
     */
    constructor() FunctionsClient(router) ConfirmedOwner(msg.sender) {}

    /**
     * @notice Sends an HTTP request for character information
     * @param subscriptionId The ID for the Chainlink subscription
     * @param args The arguments to pass to the HTTP request
     * @return requestId The ID of the request
     */
    function sendRequest(
        uint64 subscriptionId,
        string[] calldata args
    ) external onlyOwner returns (bytes32 requestId) {
        FunctionsRequest.Request memory req;
        req.initializeRequestForInlineJavaScript(source); // Initialize the request with JS code
        if (args.length > 0) req.setArgs(args); // Set the arguments for the request

        // Send the request and store the request ID
        s_lastRequestId = _sendRequest(
            req.encodeCBOR(),
            subscriptionId,
            gasLimit,
            donID
        );

        return s_lastRequestId;
    }

    /**
     * @notice Callback function for fulfilling a request
     * @param requestId The ID of the request to fulfill
     * @param response The HTTP response data
     * @param err Any errors from the Functions request
     */
    function fulfillRequest(
        bytes32 requestId,
        bytes memory response,
        bytes memory err
    ) internal override {
        if (s_lastRequestId != requestId) {
            revert UnexpectedRequestID(requestId); // Check if request IDs match
        }
        // Update the contract's state variables with the response and any errors
        s_lastResponse = response;
        character = string(response);
        s_lastError = err;

        // Emit an event to log the response
        emit Response(requestId, character, s_lastResponse, s_lastError);
    }
}
  • To write a Chainlink Functions consumer contract, your contract must import FunctionsClient.sol and FunctionsRequest.sol. You can read the API references: FunctionsClient and FunctionsRequest.

    These contracts are available in an NPM package so that you can import them from within your project.

    import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/FunctionsClient.sol";
    import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/libraries/FunctionsRequest.sol";
  • Use the FunctionsRequest.sol library to get all the functions needed for building a Chainlink Functions request.

    using FunctionsRequest for FunctionsRequest.Request;
  • The latest request ID, latest received response, and latest received error (if any) are defined as state variables:

    bytes32 public s_lastRequestId;
    bytes public s_lastResponse;
    bytes public s_lastError;
  • We define the Response event that your smart contract will emit during the callback

    event Response(bytes32 indexed requestId, string character, bytes response, bytes err);
  • The Chainlink Functions router address and donID are hardcoded for Polygon Mumbai. Check the supported networks page to try the code sample on another testnet.

  • The gasLimit is hardcoded to 300000, the amount of gas that Chainlink Functions will use to fulfill your request.

  • The JavaScript source code is hardcoded in the source state variable. For more explanation, read the JavaScript code section.

  • Pass the router address for your network when you deploy the contract:

    constructor() FunctionsClient(router)
  • The two remaining functions are:

    • sendRequest for sending a request. It receives the subscription ID and list of arguments to pass to the source code. Then:

      • It uses the FunctionsRequest library to initialize the request and add the source code and arguments. You can read the API Reference for Initializing a request and adding arguments.

        FunctionsRequest.Request memory req;
        req.initializeRequestForInlineJavaScript(source);
        if (args.length > 0) req.setArgs(args);
      • It sends the request to the router by calling the FunctionsClient sendRequest function. You can read the API reference for sending a request. Finally, it stores the request id in s_lastRequestId and returns it.

        s_lastRequestId = _sendRequest(
            req.encodeCBOR(),
            subscriptionId,
            gasLimit,
            jobId
        );
        return s_lastRequestId;

        Note: _sendRequest accepts requests encoded in bytes. Therefore, you must encode it using encodeCBOR.

  • 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 s_lastResponse and s_lastError, parses the response from bytes to string to fetch the character name before emitting the Response event.

    s_lastResponse = response;
    character = string(response);
    s_lastError = err;
    emit Response(requestId, s_lastResponse, s_lastError);

JavaScript code

const characterId = args[0];
const apiResponse = await Functions.makeHttpRequest({
  url: `https://swapi.dev/api/people/${characterId}/`,
});
if (apiResponse.error) {
  throw Error("Request failed");
}
const { data } = apiResponse;
return Functions.encodeString(data.name);

This JavaScript source code uses Functions.makeHttpRequest to make HTTP requests. The source code calls the https://swapi.dev/ API to request a Star Wars character name. If you read the Functions.makeHttpRequest documentation and the Star Wars API documentation, you notice that URL has the following format where $characterId is provided as parameter when making the HTTP request:

url: `https://swapi.dev/api/people/${characterId}/`

To check the expected API response for the first character, you can directly paste the following URL in your browser https://swapi.dev/api/people/1/ or run the curl command in your terminal:

curl -X 'GET' \
  'https://swapi.dev/api/people/1/' \
  -H 'accept: application/json'

The response should be similar to the following example:

{
  "name": "Luke Skywalker",
  "height": "172",
  "mass": "77",
  "hair_color": "blond",
  "skin_color": "fair",
  "eye_color": "blue",
  "birth_year": "19BBY",
  "gender": "male",
  "homeworld": "https://swapi.dev/api/planets/1/",
  "films": [
    "https://swapi.dev/api/films/1/",
    "https://swapi.dev/api/films/2/",
    "https://swapi.dev/api/films/3/",
    "https://swapi.dev/api/films/6/"
  ],
  "species": [],
  "vehicles": ["https://swapi.dev/api/vehicles/14/", "https://swapi.dev/api/vehicles/30/"],
  "starships": ["https://swapi.dev/api/starships/12/", "https://swapi.dev/api/starships/22/"],
  "created": "2014-12-09T13:50:51.644000Z",
  "edited": "2014-12-20T21:17:56.891000Z",
  "url": "https://swapi.dev/api/people/1/"
}

Now that you understand the structure of the API. Let's delve into the JavaScript code. The main steps are:

  • Fetch characterId from args. Args is an array. The characterId is located in the first element.
  • Make the HTTP call using Functions.makeHttpRequest and store the response in apiResponse.
  • Throw an error if the call is not successful.
  • The API response is located at data.
  • Read the name from the API response data.name and return the result as a buffer using the Functions.encodeString helper function. Because the name is a string, we use encodeString. For other data types, you can use different data encoding functions. Note: Read this article if you are new to Javascript Buffers and want to understand why they are important.

What's next

Stay updated on the latest Chainlink news