> For the complete documentation index, see [llms.txt](https://cll-devrel.gitbook.io/bootcamp-2024/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cll-devrel.gitbook.io/bootcamp-2024/9.-off-chain-data-with-chainlink-functions/city-weather-on-chainlink-functions.md).

# City Weather on Chainlink Functions

### Remix

Go to Remix and create a new Solidity file by 1. going to the “FILE EXPLORER” and 2. clicking the “Create new file” icon:

<div align="left"><figure><img src="/files/nkat1CCxZ5gdaJPqMfp5" alt=""><figcaption><p>Create a new Solidity file</p></figcaption></figure></div>

Name the new file `WeatherFunctions.sol`:

<figure><img src="/files/1HK2UySoWG9cHTWWWIof" alt=""><figcaption><p>Create a new Solidity file</p></figcaption></figure>

The code is available at [GitHub](https://github.com/solangegueiros/chainlink-bootcamp-2024/blob/main/WeatherFunctions.sol) and also below:

{% code title="WeatherFunctions.sol" %}

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

// Deploy on Fuji

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

contract WeatherFunctions is FunctionsClient {
    using FunctionsRequest for FunctionsRequest.Request;

    // State variables to store the last request ID, response, and error
    bytes32 public lastRequestId;
    bytes public lastResponse;
    bytes public lastError;

    struct RequestStatus {
        bool fulfilled; // whether the request has been successfully fulfilled
        bool exists; // whether a requestId exists
        bytes response;
        bytes err;
    }
    mapping(bytes32 => RequestStatus) public requests; /* requestId --> requestStatus */          
    bytes32[] public requestIds;

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

    // Hardcoded for Fuji
    // Supported networks https://docs.chain.link/chainlink-functions/supported-networks
    address router = 0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0;
    bytes32 donID =
        0x66756e2d6176616c616e6368652d66756a692d31000000000000000000000000;

    //Callback gas limit
    uint32 gasLimit = 300000;

    // Your subscription ID.
    uint64 public subscriptionId;

    // JavaScript source code
    string public 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);";
    string public lastCity;    
    string public lastTemperature;
    address public lastSender;

    struct CityStruct {
        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;      
    }

    function getTemperature(
        string memory _city
    ) external returns (bytes32 requestId) {

        string[] memory args = new string[](1);
        args[0] = _city;

        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
        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 requested
    function fulfillRequest(
        bytes32 requestId,
        bytes memory response,
        bytes memory err
    ) internal override {
        require(requests[requestId].exists, "request not found");

        lastError = err;
        lastResponse = response;

        requests[requestId].fulfilled = true;
        requests[requestId].response = response;
        requests[requestId].err = err;

        string memory auxCity = request_city[requestId];
        lastTemperature = string(response);
        cities[cityIndex[auxCity]].temperature = lastTemperature;
        cities[cityIndex[auxCity]].timestamp = block.timestamp;

        // Emit an event to log the response
        emit Response(requestId, lastTemperature, lastResponse, lastError);
    }

	function getCity(string memory city) public view returns (CityStruct memory) {
    	return cities[cityIndex[city]];
	}

	function listAllCities() public view returns (CityStruct[] memory) {
    	return cities;
	}

    function listCities(uint start, uint end) public view returns (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;
    }

}
```

{% endcode %}

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:

<div align="left"><figure><img src="/files/Qzr3Xqs4HP8WHvhQT58L" alt=""><figcaption></figcaption></figure></div>

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:

<figure><img src="/files/SRr8WvHkTw5GUdeaR2Hb" alt=""><figcaption><p>Enter your Chainlink Subscription ID and click "transact"</p></figcaption></figure>

MetaMask will once again pop-up and ask you to confirm the transaction:

<div align="left"><figure><img src="/files/AU6UPI6TT7IGjC1ccc7l" alt=""><figcaption><p>Confirm the transaction</p></figcaption></figure></div>

Once deployed, we need to add this contract's address as a consumer. Copy the contracts address from here in Remix:

<div align="left"><figure><img src="/files/hPNJcN2TeLD879yMYbdg" alt=""><figcaption><p>Copy your contract's address</p></figcaption></figure></div>

***

### You Subscription on Chainlink Functions

Navigate back to [Chainlink Functions](https://functions.chain.link/) and click the "Add consumer" button on the far right:

{% hint style="info" %}
You can have multiple consumer contracts in a single Chainlink Functions Subscription (up to 100)

[Chainlink Functions Service Limits](https://docs.chain.link/chainlink-functions/resources/service-limits)
{% endhint %}

<figure><img src="/files/5CXy6KrdxN7x05sCtdj0" alt=""><figcaption><p>Time to add a consumer</p></figcaption></figure>

Now 1. paste in your `WeatherFunctions.sol` deployed address and then 2. click the “Add consumer” blue button:

<figure><img src="/files/awvRkLJv4KKbQ1tuHo2g" alt=""><figcaption><p>Paste in your consumer address and add the consumer</p></figcaption></figure>

MetaMask will pop-up for you to confirm the transaction:

<figure><img src="/files/u0pKrodAuJCGRmevuyql" alt=""><figcaption><p>Confirm the transaction</p></figcaption></figure>

Once the transaction is confirmed, refresh the page you will now see your new consumer:

<figure><img src="/files/Q6AKuynBsZsxrd5RxJ7Z" alt=""><figcaption><p>Successfully added another consumer to your subscription</p></figcaption></figure>

Back in Remix with our deployed contract `WeatherFunctions.sol`, click the drop down arrow next to `getTemperature`:

<div align="left"><figure><img src="/files/PbatO2iM026BKllyMSFF" alt=""><figcaption><p>Click the down arrow</p></figcaption></figure></div>

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:

<figure><img src="/files/fJ055wnszeVMAYQyz5Dk" alt=""><figcaption><p>Confirm the transaction</p></figcaption></figure>

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:

<div align="left"><figure><img src="/files/pqXkT4zGiC959ksjbP4I" alt=""><figcaption><p>You get London's weather!</p></figcaption></figure></div>

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)! \
\
&#x20;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:

<div align="left"><figure><img src="/files/JOnel0iuqlS03IVVGCAp" alt=""><figcaption><p>Weather in Sao Paulo and our history</p></figcaption></figure></div>

***

Here are more resources to help you with examples and guides to get a solid expert foundation on using Chainlink functions:

* [11 Ways To Use Chainlink Functions in Your Decentralized Applications](https://blog.chain.link/ways-to-use-chainlink-functions/)
* [Chainlink Dev Hub | Functions resources](https://dev.chain.link/products/functions)
* [Deep Dive Into Chainlink Functions | Constellation](https://www.youtube.com/watch?v=vt0OuOm4-aY)
* [useChainlinkFunctions()](https://usechainlinkfunctions.com/)
