Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace external adapters with chainlink functions #6

Open
yogesh0509 opened this issue May 4, 2023 · 9 comments
Open

Replace external adapters with chainlink functions #6

yogesh0509 opened this issue May 4, 2023 · 9 comments
Assignees
Labels
enhancement New feature or request Level 3 WoB (45 pts) solidity

Comments

@yogesh0509
Copy link
Owner

Replace external adapters with chainlink functions to fetch live data.

@yogesh0509 yogesh0509 added enhancement New feature or request chainlink labels May 4, 2023
@EthixLucifer
Copy link

@yogesh0509 i would like to work on this issue...

@yogesh0509
Copy link
Owner Author

Hey, @EthixLucifer , can you share your approach for this issue?

@yogesh0509 yogesh0509 added the Level 3 WoB (45 pts) label Sep 26, 2024
@EthixLucifer
Copy link

EthixLucifer commented Sep 27, 2024

My approach is to review the current implementation of external adapters located in the server/external_adapter directory.
Identify the functions and data fetched by these adapters, especially how they interact with smart contracts.
Define Chainlink Functions:

Create Chainlink Functions that replicate the behavior of the existing external adapters. These functions should fetch the same data and return it in the required format.

this is the rough overview of how the function will look like....


// Create a function to fetch player details
const createRequest = async (input, callback) => {
  const jobRunID = input.id
  const playerId = input.data.playerId
  const url = `https://unofficial-cricbuzz.p.rapidapi.com/players/get-info`

  const headers = {
    'X-RapidAPI-Key': "XXXX",
    'X-RapidAPI-Host': 'unofficial-cricbuzz.p.rapidapi.com'
  }

  const params = {
    playerId
  }

  const config = {
    url,
    headers,
    params
  }

  Requester.request(config)
    .then(response => {
      callback(response.status, Requester.success(jobRunID, response))
    })
    .catch(error => {
      callback(500, Requester.errored(jobRunID, error))
    })
}


but due to lack of documentation setting up the node is facing issues.... can you share how to setup the chainlink node for this specific project..

@yogesh0509
Copy link
Owner Author

yogesh0509 commented Sep 27, 2024

I followed this documentation to run my own chainlink node. However this might not be very crucial to your task as before new contracts are deployed, current external adapters are not going to work as they are configured for mumbai testnet which was deprecated long before. You can test your chainlink functions here

@yogesh0509
Copy link
Owner Author

You will also need to make some changes in the smart contract to replace the existing API calls with chainlink functions. Can you also share a brief approach for it?

@EthixLucifer
Copy link

the changes in the smart contract would be as follows, so we will make the smart contract make the call directly to the chainlink function instead of external adapter and remove the dependency on external adapters.

This is the simple contract where I've used Amoy testnet to deploy the contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {FunctionsClient} from "@chainlink/[email protected]/src/v0.8/functions/v1_0_0/FunctionsClient.sol";
import {ConfirmedOwner} from "@chainlink/[email protected]/src/v0.8/shared/access/ConfirmedOwner.sol";
import {FunctionsRequest} from "@chainlink/[email protected]/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol";

/**
 * @title PlayerRankFetcher
 * @notice A simple contract to fetch a player's rank using Chainlink Functions
 */
contract PlayerRankFetcher is FunctionsClient, ConfirmedOwner {
    using FunctionsRequest for FunctionsRequest.Request;

    bytes32 public s_lastRequestId;
    bytes public s_lastResponse;
    bytes public s_lastError;

    string public playerRank;

    error UnexpectedRequestID(bytes32 requestId);

    event RankFetched(bytes32 indexed requestId, string rank);

    address router = 0xb83E47C2bC239B3bf370bc41e1459A34b41238D0;
    string source =
        "const playerId = args[0];"
        "const apiKey = args[1];"
        "const options = {"
        "  url: 'https://unofficial-cricbuzz.p.rapidapi.com/players/get-info',"
        "  method: 'GET',"
        "  params: { playerId: playerId },"
        "  headers: {"
        "    'x-rapidapi-key': apiKey,"
        "    'x-rapidapi-host': 'unofficial-cricbuzz.p.rapidapi.com'"
        "  }"
        "};"
        "const apiResponse = await Functions.makeHttpRequest(options);"
        "if (apiResponse.error) {"
        "  throw new Error('Request failed: ' + apiResponse.error);"
        "}"
        "const playerInfo = apiResponse.data;"
        "const testRank = playerInfo.currRank.bat.testRank;"
        "if (!testRank || isNaN(testRank)) {"
        "  throw new Error('Test rank is not a valid number');"
        "}"
        "return Functions.encodeUint256(Number(testRank));";

    uint32 gasLimit = 300000;
    bytes32 donID =
        0x66756e2d657468657265756d2d7365706f6c69612d3100000000000000000000;

    constructor() FunctionsClient(router) ConfirmedOwner(msg.sender) {}

    function fetchPlayerRank(
        uint64 subscriptionId,
        string[] calldata args
    ) external onlyOwner returns (bytes32 requestId) {
        FunctionsRequest.Request memory req;
        req.initializeRequestForInlineJavaScript(source);
        req.setArgs(args);

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

        return s_lastRequestId;
    }

    function fulfillRequest(
        bytes32 requestId,
        bytes memory response,
        bytes memory err
    ) internal override {
        if (s_lastRequestId != requestId) {
            revert UnexpectedRequestID(requestId);
        }

        s_lastResponse = response;
        s_lastError = err;

        playerRank = string(response);

        emit RankFetched(requestId, playerRank);
    }

    function getLastRank() public view returns (string memory) {
        return playerRank;
    }
}

you can load this contract at address: 0x144e8f069d2f2d05e8d9cbbb9bd4135b4aca5311

once loaded execute the fetchplayerRank function and pass the arguments the chain ID for the chainlink is: 355 and pass the playerId and rapid API key.

execute this function in the chainlink functions playground


//Chainlink Function
// This script will fetch the player's information using Chainlink Functions

const playerId = args[0];
const apikey = args[1];
const options = {
  url: 'https://unofficial-cricbuzz.p.rapidapi.com/players/get-info',
  method: 'GET',
  params: { playerId: playerId },
  headers: {
    'x-rapidapi-key': `${apikey}`,
    'x-rapidapi-host': 'unofficial-cricbuzz.p.rapidapi.com'
  }
};

// Perform the API request using Chainlink Functions helper method
const apiResponse = await Functions.makeHttpRequest(options);

// Check for errors
if (apiResponse.error) {
  throw new Error('Request failed: ' + apiResponse.error);
}

// Log the response data for debugging
console.log('API Response:', apiResponse.data);

// Ensure the response contains data
if (!apiResponse.data) {
  throw new Error('API response data is undefined or null.');
}

// Extract the player's Test rank from the response
const playerInfo = apiResponse.data;
const testRank = playerInfo.currRank.bat.testRank;

// Check if the rank is a valid number
if (!testRank || isNaN(testRank)) {
  throw new Error('Test rank is not a valid number');
}

// Log the player's Test rank
console.log('Player Test Rank:', testRank);

// Return the Test rank as a uint256
return Functions.encodeUint256(Number(testRank));

@EthixLucifer
Copy link

EthixLucifer commented Sep 29, 2024

this is just to test the contract fetches the rank, in prod environment dependencies on external contracts, i.e

 import "./Auction.sol";
import "./interfaces/IPIC.sol";

will be taken care of..

@yogesh0509
Copy link
Owner Author

@EthixLucifer This looks really good. I am assigning this issue to you. Make changes in a separate branch and raise the PR.

@yogesh0509
Copy link
Owner Author

@EthixLucifer Any updates??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Level 3 WoB (45 pts) solidity
Projects
None yet
Development

No branches or pull requests

2 participants