Got a cool example to share? Get listed here! Submit it now.

useChainlinkFunctions()

Collection of community submitted examples for Chainlink Functions

Get inspired from quick examples

Chainlink Functions, a new self-service platform that allows anyone to write serverless code to fetch any data from any API and run custom compute on Chainlink's network. Learn from examples created by the community or contribute your own!
Learn more about Chainlink Functions

Fetch Discord Upvote Data

Submitted by:
Sam Demaree
This function retrieves the number of upvotes a Discord member has received in the past 24 hours. *Note: ChatGPT was used to demonstrate that non-developers can also participate.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // This function retrieves the number of upvotes a Discord member has received in the past 24 hours using the Discord API. const getDiscordUpvotes = async (memberId, apiKey, guildId, channelId, timeRangeMs) => { const endpoint = 'https://discord.com/api/v9' const timeRangeSec = Math.round(timeRangeMs / 1000) const time24HoursAgo = Math.round((Date.now() - timeRangeMs) / 1000) const headers = { 'Authorization': `Bot ${apiKey}`, 'Content-Type': 'application/json' } const config = { method: 'GET', headers: headers, url: `${endpoint}/guilds/${guildId}/audit-logs?limit=100&user_id=${memberId}&before=${time24HoursAgo}&action_type=MESSAGE_DELETE` } const response = await Functions.makeHttpRequest(config) if (response.error) { throw new Error(response.response.data.message) } const auditLogs = response.data.audit_log_entries let upvotes = 0 for (let i = 0; i < auditLogs.length; i++) { const log = auditLogs[i] if (log.action_type === 72 && log.target_id === channelId && log.created_at >= time24HoursAgo - timeRangeSec) { upvotes++ } } return Functions.encodeUint256(upvotes) }

US election results from AP (Associated Press) API

Submitted by:
Karen Stepanyan
This Function returns the winner of the US election for a given date. It uses the AP (Associated Press) API to get the results. The date is the only required parameter. API key is the only required secret.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 // Chainlink Function to get election results from AP (Associated Press) API. Date and API key are the only required parameters const getReportingUnit = (reportingUnits, statePostal) => { const level = statePostal === 'US' ? 'national' : 'state' const reportingUnit = reportingUnits.find((ru) => ru.level === level) if (!reportingUnit) { throw new Error('Cannot find reporting unit') } return reportingUnit } const getReportingUnitWinner = (reportingUnit) => { for (const candidate of reportingUnit.candidates) { if (candidate.winner === 'X') { return candidate } } throw new Error('Candidate not found') } const date = args[0] // The date of the election formatted as YYYY-MM-DD const statePostal = args[1] // The state's two-letter code e.g CO. `US` to get the results of a nationwide election const raceID = args[2] // AP-assigned race ID. Should be used with `statePostal` const raceType = args[3] || 'G' // The race type the election is for. The race type can be `D(Dem Primary)`, `R(GOP Primary)`, `G(General)`, `E(Dem Caucus)`, `S(GOP Caucus)`, `X(Open Primary or special use cases)` const resultsType = args[4] || 'L' // The type of results to return. `L` for live results, `T` for test results if (!secrets.apikey) { throw new Error('Missing AP API key') } const params = { level: statePostal === 'US' ? 'national' : 'state', raceTypeID: raceType, format: 'json', winner: 'X', resultsType: resultsType, apikey: secrets.apikey, } if ((statePostal && !raceID) || (!statePostal && raceID)) { throw new Error('Both statePostal and raceID are required if one is provided') } if (statePostal) { params.statePostal = statePostal } if (raceID) { params.raceID = raceID } const config = { url: `https://api.ap.org/v3/elections/${date}`, params } const response = await Functions.makeHttpRequest(config) const races = response.data.races if (races.length === 0) { throw new Error('Could not find any races') } if (races.length > 1) { throw new Error('Finding the winner from multiple races is not supported') } const race = races[0] const reportingUnit = getReportingUnit(race.reportingUnits, statePostal) const raceWinner = getReportingUnitWinner(reportingUnit) return Functions.encodeString(JSON.stringify(raceWinner))

Aggregate the ERC20 balance of an address across multiple chains

Submitted by:
polarzero
Find the balance of a user for a specific ERC20 token across the specified chains, and return the total balance. This balance, for example, could be used immediately in the callback function to approve or deny the user access to specific functions in the contract.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 // https://github.com/polar0/cross-chain-ERC20-balance-verification/blob/main/implementation/verify-balances.js // The address to check the balances of const userAddress = args[0] // The chains to check, formatted as: // name:tokenAddress,name:tokenAddress... const tokens = args[1].split(",").map((tokenAddress) => { const [chain, address] = tokenAddress.split(":") return { chain, address } }) // Verify if there is indeed a secret (RPC URL) for each chain tokens.forEach((token) => { if (!secrets[token.chain]) { throw new Error(`No secret found for chain ${token.chain}`) } }) // Prepare requests for each chain const requests = tokens.map((token, index) => { return Functions.makeHttpRequest({ url: secrets[token.chain], method: "POST", data: { id: index, jsonrpc: "2.0", method: "eth_call", params: [ { to: token.address, // The signature of 'balanceOf(address)' + the user address without the 0x prefix data: "0x70a08231000000000000000000000000" + userAddress.slice(2), }, "latest", ], }, }) }) // Wait for all requests to finish const responses = await Promise.all(requests) // Parse responses const balances = responses.map((response) => { // Convert the result to a number return parseInt(response.data.result, 16) ?? 0 }) // Sum all balances const totalBalance = balances.reduce((a, b) => a + b, 0) // Return the total balance of the user return Functions.encodeUint256(totalBalance)

Find the Best DEX Trade Value for a Given Asset Pair

Submitted by:
Max Melcher
This example shows how to return the best DEX trade value for a give asset pair using Paraswap DEX Aggregator

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // Decimals can be passed from the token contract decimals() function const srcToken = args[0] // Token source (selling) const srcDecimals = args[1] const destAsset = args[2] //Token destination (buying) const destDecimals = args[3] const amount = args[4] // Amount of source token to trade // Pull from the Paraswap DEX Aggregator router const paraswapRequest = await Functions.makeHttpRequest({ url: `https://apiv5.paraswap.io/prices?srcToken=${srcToken}&srcDecimals=${srcDecimals}&destToken=${destAsset}&destDecimals=${destDecimals}&amount=${amount}&network=1`, }) if (!paraswapRequest.error) { console.log("Optimal trade route found!") console.log( `Swap found to exchange ${ 10 ** -paraswapRequest.data.priceRoute.srcDecimals * parseInt(paraswapRequest.data.priceRoute.srcAmount) } of ${paraswapRequest.data.priceRoute.srcToken} into ${ 10 ** -paraswapRequest.data.priceRoute.destDecimals * parseInt(paraswapRequest.data.priceRoute.destAmount) } of ${paraswapRequest.data.priceRoute.destToken}` ) //Sample Output: "Swap found to exchange 1 of 0x514910771af9ca656af840dff83e8264ecf986ca into 6.732330036871376 of 0x6b175474e89094c44da98b954eedeac495271d0f" console.log(`${paraswapRequest.data.priceRoute.bestRoute.length} best route(s) found:`) //If direct swap is found with one pool return that pool address if (paraswapRequest.data.priceRoute.bestRoute[0].percent == 100) { console.log( `One direct route found through ${paraswapRequest.data.priceRoute.bestRoute[0].swaps[0].swapExchanges[0].exchange}` ) //Sample Output: One direct route found through UniswapV2 console.log(paraswapRequest.data.priceRoute.bestRoute[0].swaps[0].swapExchanges[0].data) /* Sample Output: { router: '0xF9234CB08edb93c0d4a4d4c70cC3FfD070e78e07', path: [ '0x514910771af9ca656af840dff83e8264ecf986ca', '0x6b175474e89094c44da98b954eedeac495271d0f' ], factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', initCode: '0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f', feeFactor: 10000, pools: [ { address: '0x6D4fd456eDecA58Cf53A8b586cd50754547DBDB2', fee: 30, direction: true } ], gasUSD: '2.735657' } */ } } else { console.log("Paraswap Request error") console.log({ ...paraswapRequest }) } return Functions.encodeUint256(parseInt(paraswapRequest.data.priceRoute.destAmount))

Fetch result of soccer match from Sportsdata.io

Submitted by:
Karen Stepanyan
The function fetches the result of soccer match. Required arguments are match date and abbreviations of team names

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 // Chainlink function to get the winner of soccer match. Possible return values are abbreviations of team names or 'Draw' const date = args[0] // Match date. basic date format YYYY-MM-DD. for example 2023-01-28 let teams = args[1] // competing teams in following format TEAM1/TEAM2. for example AST/LEI if (!secrets.soccerApiKey) { throw Error("Sportsdata.io API KEY is required") } const config = { url: `https://api.sportsdata.io/v3/soccer/scores/json/GamesByDate/${date}?key=${secrets.soccerApiKey}` } const response = await Functions.makeHttpRequest(config) const allMatches = response.data; const match = allMatches.find(match => { const playingTeams = `${match.AwayTeamKey}/${match.HomeTeamKey}`.toUpperCase() const playingTeamsReversed = `${match.HomeTeamKey}/${match.AwayTeamKey}`.toUpperCase() if (teams.toUpperCase() === playingTeams || teams.toUpperCase() === playingTeamsReversed) { return true } }) if (!match) { throw new Error('Match not found for given arguments') } if (match.Winner === 'Scrambled') { throw new Error('Data is scrambled, use production API Key') } let result; if (match.Winner === 'AwayTeam') { result = match.AwayTeamKey } else if (match.Winner === 'HomeTeam') { result = match.HomeTeamKey } else if (match.Winner === 'Draw') { result = 'Draw' } if (!result) { throw new Error('Could not get the winner team.') } return Functions.encodeString(result)

Prompt AI for a response

Submitted by:
Patrick Collins
Ask OpenAI (or any AI model you want to interact with) for information on-chain.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const prompt = args[0] if ( !secrets.openaiKey ) { throw Error( "Need to set OPENAI_KEY environment variable" ) } // example request: // curl https://api.openai.com/v1/completions -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_API_KEY" -d '{"model": "text-davinci-003", "prompt": "Say this is a test", "temperature": 0, "max_tokens": 7} // example response: // {"id":"cmpl-6jFdLbY08kJobPRfCZL4SVzQ6eidJ","object":"text_completion","created":1676242875,"model":"text-davinci-003","choices":[{"text":"\n\nThis is indeed a test","index":0,"logprobs":null,"finish_reason":"length"}],"usage":{"prompt_tokens":5,"completion_tokens":7,"total_tokens":12}} const openAIRequest = Functions.makeHttpRequest({ url: "https://api.openai.com/v1/completions", method: "POST", headers: { 'Authorization': `Bearer ${secrets.openaiKey}` }, data: { "model": "text-davinci-003", "prompt": prompt, "temperature": 0, "max_tokens": 7 } }) const [openAiResponse] = await Promise.all([ openAIRequest ]) console.log("raw response", openAiResponse) const result = openAiResponse.data.choices[0].text return Functions.encodeString(result)

Read cross-chain information

Submitted by:
Patrick Collins
The function reads the supply APY rate of depositing WETH into AaveV3 on Polygon

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 // This example shows how to make a decentralized price feed using multiple APIs // Arguments can be provided when a request is initated on-chain and used in the request source code as shown below const contractAddress = args[0] const encodedAbiFunctionCall = args[1] if ( !secrets.polygonKey ) { throw Error( "Need to set POLYGON_RPC_URL environment variable" ) } // curl --data '{"method":"eth_call","params":[{"to":"0x794a61358D6845594F94dc1DB02A252b5b4814aD","data":"0x35ea6a750000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f619"},"latest"],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST $POLYGON_RPC_URL // example response: // {"jsonrpc":"2.0","id":1,"result":"0x000000000000000000000003e80000069140000039cb03e805122904203a1f400000000000000000000000000000000000000000033e9fbcc201bc653e561a5300000000000000000000000000000000000000000002542e73dd9e8a5aecdb2a0000000000000000000000000000000000000000034895c6e6312a938da89522000000000000000000000000000000000000000000123f39e6ba5158357302ea0000000000000000000000000000000000000000004a723dc6b40b8a9a0000000000000000000000000000000000000000000000000000000000000063e965ca0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000e50fa9b3c56ffb159cb0fca61f5c9d750e8128c8000000000000000000000000d8ad37849950903571df17049516a5cd4cbe55f60000000000000000000000000c84331e39d6658cd6e6b9ba04736cc4c473435100000000000000000000000003733f4e008d36f2e37f0080ff1c8df756622e6f00000000000000000000000000000000000000000000000001e758ee6c676a3f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"} // To make an HTTP request, use the Functions.makeHttpRequest function // Functions.makeHttpRequest function parameters: // - url // - method (optional, defaults to 'GET') // - headers: headers supplied as an object (optional) // - params: URL query parameters supplied as an object (optional) // - data: request body supplied as an object (optional) // - timeout: maximum request duration in ms (optional, defaults to 10000ms) // - responseType: expected response type (optional, defaults to 'json') // Ideally, you'd use multiple RPC URLs so we don't have to trust just one const polygonReadRequest = Functions.makeHttpRequest({ url: secrets.polygonKey, method: "POST", data: { "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": contractAddress, data: encodedAbiFunctionCall }, "latest" ], "id": 1 } }) // First, execute all the API requests are executed concurrently, then wait for the responses const [polygonResponse] = await Promise.all([ polygonReadRequest ]) console.log("raw response", polygonResponse) // take the "0x" off the front of the hex string const result = polygonResponse.data.result.slice(2) // loop through result and convert each 64 characters to a number const startingIndex = 64 * 2 const supplyApy = "0x" + result.slice(startingIndex, startingIndex + 64) // convert the hex supplyApy to a number const supplyApyNumber = parseInt(supplyApy, 16) // This number is returned as a RAY, so we'd divide by 1e27, or 1e25 to get a percentage console.log("WETH Supply APY on AaveV3 in Polygon: ", (supplyApyNumber / 1e25), "%") // The source code MUST return a Buffer or the request will return an error message // Use one of the following functions to convert to a Buffer representing the response bytes that are returned to the client smart contract: // - Functions.encodeUint256 // - Functions.encodeInt256 // - Functions.encodeString // Or return a custom Buffer for a custom byte encoding // return Functions.encodeUint256(Math.round(medianPrice * 100)) return Functions.encodeUint256(supplyApyNumber)

Fetch outcome of off-chain Snapshot.org vote

Submitted by:
ChainLinkGod
The function fetches the outcome of an off-chain Snapshot.org vote proposal using the GraphQL API. Takes into account if the vote has closed and has met quorum. Gas efficient solution for DAOs.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 const proposalID = args[0] if (!proposalID) { throw Error("Proposal ID is required") } const config = { url: "https://hub.snapshot.org/graphql?", method: "POST", headers: { 'content-type': 'application/json' }, params: { operationName: "Proposal", query: `query Proposal {\n proposal(id:"${proposalID}") {\n id\n votes\n scores\n choices\n state\n scores_total\n quorum\n}\n}`, variables: null, }, } const response = await Functions.makeHttpRequest(config) const state = response.data.data.proposal.state const totalScore = response.data.data.proposal.scores_total const quorum = response.data.data.proposal.quorum if (state !== 'closed') { return Functions.encodeString('Vote not ended') } if (totalScore < quorum) { return Functions.encodeString('Quorum not met') } const scores = response.data.data.proposal.scores const choices = response.data.data.proposal.choices const highestIndex = scores.indexOf(Math.max(...scores)); return Functions.encodeString(choices[highestIndex])

Financial metric data for dApps and blockchains sourced from Token Terminal

Submitted by:
ChainLinkGod
This Function fetches metric data from the Token Terminal API for a specific project. Supported metrics include revenue, fees, earnings, active users, TVL, volume, supply, and more. Projects includes both dApps and blockchains. Optional parameter for specific date. Requires Token Terminal Pro subscription to obtain API key.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const metric = args[0] // valid metric id that can be found on https://api.tokenterminal.com/v2/metrics const project = args[1] // project id const date = args[2] // optional date. format YYYY-MM-DD. For example 2023-02-10 const apiKey = secrets.API_KEY; if (!apiKey) { throw Error("Tokenterminal API Key is required") } const config = { url: `https://api.tokenterminal.com/v2/metrics/${metric}?project_ids=${project}`, headers: { 'Authorization': `Bearer ${apiKey}` } } const response = await Functions.makeHttpRequest(config) if (response.error) { throw new Error(response.response.data.message) } let data; if (date) { data = response.data.data.find(d => d.timestamp.includes(date)) }else { data = response.data.data[0] } const result = Math.round(data.value * 100) return Functions.encodeUint256(result)

Obtain outcome of off-chain vote

Submitted by:
mykcryptodev
This function fetches the final outcome of an off-chain vote on the Snapshot.org platform

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const proposalId = args[0] // Use snapshot's graphql API to get the final vote outcome const snapshotRequest = () => Functions.makeHttpRequest({ url: `https://hub.snapshot.org/graphql`, method: "POST", data: { query: `{ proposal(id: "${proposalId}") { choices scores scores_state } }`, }, }) const { data, error } = await snapshotRequest() if (error) { throw Error("Snapshot request failed") } const { proposal } = data.data const { choices, scores, scores_state } = proposal if (scores_state !== "final") { throw Error("Snapshot vote is not final") } const winningChoice = choices[scores.indexOf(Math.max(...scores))] return Functions.encodeString(winningChoice)

Fetch and return available balance of Stripe account

Submitted by:
Karen Stepanyan
This function will fetch Stripe account available balance of particular currency.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const apiKey = secrets.API_KEY const balanceCurrency = args[0] || 'usd' if (!apiKey) { throw Error("Stripe API Key is required") } const config = { url: `https://${apiKey}@api.stripe.com/v1/balance`, } const response = await Functions.makeHttpRequest(config) const balance = response.data.available.find(c => c.currency.toLowerCase() === balanceCurrency.toLowerCase()) const balanceInCents = Math.round(balance.amount * 100) return Functions.encodeUint256(balanceInCents)

Calculate the median price of a token on Uniswap V2

Submitted by:
moonthoon
This function calculates the median price of a token that is on Uniswap V2. It works by sampling up to 4 prices over a given time period then chooses the median value

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 // Max sample size is 4 due to 5 http request limit const SAMPLE_SIZE = 4 // The number of decimals the price in USD is formatted to const DECIMALS = 18 // A block buffer to take into consideration the synchronization of the subgraph const GRAPH_BLOCK_BUFFER = 50 const AVG_SECONDS_PER_BLOCK = 12 // Token address const token = args[0].toLowerCase(); // Pair address const pair = args[1] // Period in seconds const period = args[2] const blockRange = period / AVG_SECONDS_PER_BLOCK if (!secrets.rpc) { throw Error("\"rpc\" environment variable not set") } const blockNumberResponse = await Functions.makeHttpRequest({ url: secrets.rpc, method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json", }, data: JSON.stringify({ jsonrpc: "2.0", method: "eth_blockNumber", params: [], id: "1", }), }) if (blockNumberResponse.error) { throw Error("Unable to fetch current block number") } const blockNumber = parseInt(blockNumberResponse.data.result, 16) - GRAPH_BLOCK_BUFFER const fetchPrice = (blockNumber) => Functions.makeHttpRequest({ url: "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2", method: "POST", data: { query: `{ pair(id: "${pair}", block: {number: ${blockNumber}}) { token0 { id } token1 { id } reserve0 reserve1 reserveUSD } }`, }, }) const stringToBigInt = (str) => { const splitStr = str.split(".") const decimals = splitStr[1].slice(0, DECIMALS).padEnd(DECIMALS, "0") return BigInt(`${splitStr[0]}${decimals}`) } const getPrice = async (blockNumber) => { const { error, data: { errors, data, }, } = await fetchPrice(blockNumber) if (error.error || errors) { throw Error("Unable to fetch price from subgraph") } const { pair: { token0: { id: token0 }, token1: { id: token1 }, reserve0, reserve1, reserveUSD } } = data const token0LC = token0.toLowerCase() const token1LC = token1.toLowerCase() if (token0LC !== token && token1LC !== token) { throw Error("Token not found as part of the pair") } const tokenReserveInUSD = stringToBigInt(reserveUSD) / 2n const tokenReserve = stringToBigInt(token0LC === token ? reserve0 : reserve1) return BigInt(10 ** DECIMALS) * tokenReserveInUSD / tokenReserve } const pickRandomBlock = () => { return blockNumber - Math.round(Math.random() * blockRange) } let prices = [] for (let i = 0; i < SAMPLE_SIZE; i++) { const price = await getPrice(pickRandomBlock()) prices.push(price) } const midpoint = SAMPLE_SIZE % 2 === 0 ? SAMPLE_SIZE / 2 : (SAMPLE_SIZE + 1) / 2 const median = prices[midpoint] return Functions.encodeUint256(median)

Twitter account verification with an Ethereum address

Submitted by:
polarzero
Check if a Twitter account belongs to a specific Ethereum address. This example uses the Twitter API to retrieve a user's recent tweets, and checks if they tweeted a specific message containing their address. It provides the arguments and returns the result via Chainlink Functions, which allows for prior validation of the user's ownership of the address via a signature or other method, thus performing a secure and non-intrusive verification.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 // https://github.com/polar0/twitter-verifier-chainlink-functions/blob/main/implementation/twitter-verification/functions/Functions-request-source.js // Get the arguments from the request config const twitterUsername = args[0]; // e.g. 'TwitterDev' const ethereumAddress = args[1]; // e.g. '0x1234567890123456789012345678901234567890' // The string that must be included in the latest tweets of the user for the verification to pass const requiredStringIncluded = `Verifying my Twitter account for ${ethereumAddress}`; // How many tweets to check (min 5, max 100) const MAX_RESULTS = 10; // Initialize the result to -1 (error) let result = -1; // Get the bearer token from the environment variables if (!secrets.apiKey) { throw Error( 'TWITTER_BEARER_TOKEN environment variable not set for Twitter API. Get a free one: https://developer.twitter.com/en/docs/authentication/oauth-2-0/bearer-tokens', ); } // Don't even try if the username or address is empty if (!twitterUsername || !ethereumAddress) { throw Error('Twitter username or Ethereum address is empty'); } // Prepare the API requests const twitterRequest = { // Get the user id from the provided username userIdByUsername: () => Functions.makeHttpRequest({ url: `https://api.twitter.com/2/users/by/username/${twitterUsername}`, headers: { Authorization: `Bearer ${secrets.apiKey}` }, }), // Get the latest n tweets from the user (n = MAX_RESULTS) lastTweetsByUserId: (userId) => Functions.makeHttpRequest({ url: `https://api.twitter.com/2/users/${userId}/tweets?max_results=${MAX_RESULTS}`, headers: { Authorization: `Bearer ${secrets.apiKey}` }, }), }; // First, request the user id from their username const idRes = await new Promise((resolve, reject) => { twitterRequest.userIdByUsername().then((res) => { if (!res.error) { resolve(res); } else { reject(res); } }); }); if (idRes.error) { throw Error('Twitter API request failed - coult not get user id'); } // Grab the user id const userId = idRes.data.data.id || null; // Let's be extra careful and make sure the user id is not null if (!userId) { throw Error('Twitter API request failed - user id is null'); } // Then, request the latest tweets const tweetsRes = await new Promise((resolve, reject) => { twitterRequest.lastTweetsByUserId(userId).then((res) => { if (!res.error) { resolve(res); } else { reject(res); } }); }); if (tweetsRes.error) { throw Error('Twitter API request failed - coult not get tweets'); } // It'll only get here if the request was successful const tweets = tweetsRes.data.data; const tweetTexts = tweets.map((tweet) => tweet.text); // Check if any of these tweets include the required string const res = tweetTexts.some((text) => text.toLowerCase().includes(requiredStringIncluded.toLowerCase()), ); // If it found the string, return 1, otherwise 0 result = res ? 1 : 0; // `result` can either be: // - 1 (verified) // - 0 (not verified) // - -1 (if by any chance no error was thrown, yet it could not verify) // Return the result along with the username and address, which can be parsed and split return Functions.encodeString( `${result},${twitterUsername},${ethereumAddress}`, );

Price data from multiple sources

Submitted by:
Morgan Kuphal
Retrieve the price of an asset from multiple API sources. Assets could be practially anything, incuding equities, crypto, or commodities. This example pulles from multiple different data providers (APIs) and derrives the median to return on chain via Chainlink Functions.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 const coinMarketCapCoinId = args[0]; const coinGeckoCoinId = args[1]; const coinPaprikaCoinId = args[2]; const badApiCoinId = args[3]; const scalingFactor = parseInt(args[4]); if (!secrets.apiKey) { throw Error('API_KEY environment variable not set for CoinMarketCap API. Get a free key from https://coinmarketcap.com/api/') } // OCR2DR.makeHttpRequest function parameters: // - url // - method (optional, defaults to 'GET') // - headers: headers supplied as an object (optional) // - params: URL query parameters supplied as an object (optional) // - data: request body supplied as an object (optional) // - timeout: maximum request duration in ms (optional, defaults to 10000ms) // - responseType: expected response type (optional, defaults to 'json') // Use multiple APIs & aggregate the results to enhance decentralization const coinMarketCapResponse = await OCR2DR.makeHttpRequest({ url: `https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?convert=USD&id=${coinMarketCapCoinId}`, // Get a free API key from https://coinmarketcap.com/api/ headers: { 'X-CMC_PRO_API_KEY': secrets.apiKey } }); const coinGeckoResponse = await OCR2DR.makeHttpRequest({ url: `https://api.coingecko.com/api/v3/simple/price?ids=${coinGeckoCoinId}&vs_currencies=usd`, }); const coinPaprikaResponse = await OCR2DR.makeHttpRequest({ url: `https://api.coinpaprika.com/v1/tickers/${coinPaprikaCoinId}` }); const badApiResponse = await OCR2DR.makeHttpRequest({ url: `https://badapi.com/price/symbol/${badApiCoinId}` }); const prices = []; if (!coinMarketCapResponse.error) { prices.push(coinMarketCapResponse.data.data[coinMarketCapCoinId].quote.USD.price); } else { console.log('CoinMarketCap Error'); console.log({ ...coinMarketCapResponse }); } if (!coinGeckoResponse.error) { prices.push(coinGeckoResponse.data[coinGeckoCoinId].usd); } else { console.log('CoinGecko Error'); console.log({ ...coinGeckoResponse }); } if (!coinPaprikaResponse.error) { prices.push(coinPaprikaResponse.data.quotes.USD.price); } else { console.log('CoinPaprika Error'); console.log({ ...coinPaprikaResponse }); } // A single failed API request does not cause the whole request to fail if (!badApiResponse.error) { prices.push(httpResponses[3].data.price.usd); } else { console.log('Bad API request failed. (This message is expected and just for demonstration purposes.)') } // At least 3 prices are needed to aggregate the median price if (prices.length < 3) { // If an error is thrown, it will be returned back to the smart contract throw Error('More than 1 API failed'); } const medianPrice = prices.sort((a, b) => a - b)[Math.round(prices.length / 2)]; console.log(`Median Bitcoin price: $${medianPrice.toFixed(2)}`); // Use the following functions to encode a single value: // - OCR2DR.encodeUint256 // - OCR2DR.encodeInt256 // - OCR2DR.encodeString // Or return a Buffer for a custom byte encoding return OCR2DR.encodeUint256(Math.round(medianPrice * 100));