// SPDX-License-Identifier: MITpragmasolidity 0.8.19;// Deploy on Fujiimport {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";contractWeatherFunctionsisFunctionsClient {usingFunctionsRequestforFunctionsRequest.Request;// State variables to store the last request ID, response, and errorbytes32public lastRequestId;bytespublic lastResponse;bytespublic lastError;structRequestStatus {bool fulfilled; // whether the request has been successfully fulfilledbool exists; // whether a requestId existsbytes response;bytes err; }mapping(bytes32=> RequestStatus) public requests; /* requestId --> requestStatus */bytes32[] public requestIds;// Event to log responseseventResponse(bytes32indexed requestId,string temperature,bytes response,bytes err );// Hardcoded for Fuji// Supported networks https://docs.chain.link/chainlink-functions/supported-networksaddress router =0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0;bytes32 donID =0x66756e2d6176616c616e6368652d66756a692d31000000000000000000000000;//Callback gas limituint32 gasLimit =300000;// Your subscription ID.uint64public subscriptionId;// JavaScript source codestringpublic source ="const city = args[0];""const apiResponse = await Functions.makeHttpRequest({""url: `https://wttr.in/${city}?format=3&m`,""responseType: 'text'""});""if (apiResponse.error) {""throw Error('Request failed');""}""const { data } = apiResponse;""return Functions.encodeString(data);";stringpublic lastCity; stringpublic lastTemperature;addresspublic lastSender;structCityStruct {address sender;uint timestamp;string name;string temperature; } CityStruct[] public cities;mapping(string=>uint256) public cityIndex;mapping(bytes32=>string) public request_city; /* requestId --> city*/constructor(uint64 functionsSubscriptionId) FunctionsClient(router) { subscriptionId = functionsSubscriptionId; }functiongetTemperature(stringmemory_city ) externalreturns (bytes32 requestId) {string[] memory args =newstring[](1); args[0] = _city; FunctionsRequest.Request memory req; req.initializeRequestForInlineJavaScript(source); // Initialize the request with JS codeif (args.length >0) req.setArgs(args); // Set the arguments for the request// Send the request and store the request ID lastRequestId =_sendRequest( req.encodeCBOR(), subscriptionId, gasLimit, donID ); lastCity = _city; request_city[lastRequestId] = _city; CityStruct memory auxCityStruct =CityStruct({ sender: msg.sender, timestamp:0, name: _city, temperature:"" }); cities.push(auxCityStruct); cityIndex[_city] = cities.length-1; requests[lastRequestId] =RequestStatus({ exists:true, fulfilled:false, response:"", err:"" }); requestIds.push(lastRequestId);return lastRequestId; }// Receive the weather in the city requestedfunctionfulfillRequest(bytes32 requestId,bytesmemory response,bytesmemory err ) internaloverride {require(requests[requestId].exists,"request not found"); lastError = err; lastResponse = response; requests[requestId].fulfilled =true; requests[requestId].response = response; requests[requestId].err = err;stringmemory auxCity = request_city[requestId]; lastTemperature =string(response); cities[cityIndex[auxCity]].temperature = lastTemperature; cities[cityIndex[auxCity]].timestamp = block.timestamp;// Emit an event to log the responseemitResponse(requestId, lastTemperature, lastResponse, lastError); }functiongetCity(stringmemory city) publicviewreturns (CityStructmemory) {return cities[cityIndex[city]]; }functionlistAllCities() publicviewreturns (CityStruct[] memory) {return cities; }functionlistCities(uint start,uint end) publicviewreturns (CityStruct[] memory) {if (end > cities.length) end = cities.length-1;require (start <= end,"start must <= end");uint cityCount = end - start +1; CityStruct[] memory citiesAux =new CityStruct[](cityCount);for (uint i = start; i < (end +1); i++) { citiesAux[i-start] = cities[i]; }return citiesAux; }}
As in the previous exercise, in the “DEPLOY & RUN TRANSACTIONS”, make sure your “ENVIRONMENT is on “Injected Provider - Metamask” and you are on Avalanche Fuji Testnet 43113:
Under the “DEPLOY” section, 1. paste in your Subscription ID we set up in the previous exercise and then 2. click the “transact” button to deploy our contract:
MetaMask will once again pop-up and ask you to confirm the transaction:
Once deployed, we need to add this contract's address as a consumer. Copy the contracts address from here in Remix:
You Subscription on Chainlink Functions
Navigate back to Chainlink Functions and click the "Add consumer" button on the far right:
You can have multiple consumer contracts in a single Chainlink Functions Subscription (up to 100)
Now 1. paste in your WeatherFunctions.sol deployed address and then 2. click the “Add consumer” blue button:
MetaMask will pop-up for you to confirm the transaction:
Once the transaction is confirmed, refresh the page you will now see your new consumer:
Back in Remix with our deployed contract WeatherFunctions.sol, click the drop down arrow next to getTemperature:
Fill in the 1. _city argument as London, or maybe your next vacation city, and then 2. click the “transact” button and 3. MetaMask will pop-up asking you to “Confirm” the transaction:
After the transaction is complete, there are several read functions that will return you information. You asked for the weather in London, thus the lastTemperature function is what you are looking for below:
You can try any other city again. Sao Paulo is the argument for _city (#1) and thus you would do another transaction and after completion, you now see the weather in Sao Paulo (#2)!
Click on the listAllCities function (#3), which returns a tuple of the history of what cities you have called for the current weather and it shows London and Sao Paulo:
Here are more resources to help you with examples and guides to get a solid expert foundation on using Chainlink functions: