Lesson 10 of 14
In Progress

Using Chainlink to Trigger Complex Smart Contract Logic

As a blockchain developer, you may often find yourself needing to trigger complex smart contract logic based on external data or events. While you could manually check for these events and trigger your logic manually, this can be time-consuming and prone to errors. Fortunately, there is a better way – using Chainlink to automate the process.

In this chapter, we will learn how to use Chainlink to trigger complex smart contract logic based on external data or events. We will also learn how to customize the trigger conditions and parameters to fit your specific needs. By the end of this chapter, you will have a solid understanding of how to use Chainlink to automate your smart contract logic and save time and resources.

Before we dive into using Chainlink to trigger complex smart contract logic, it is important to understand what Chainlink is and how it works.

Chainlink is a decentralized oracle network that allows smart contracts to securely access off-chain data and events. It achieves this by using a network of independent oracle nodes that act as middlemen between the smart contract and the external data source. The oracle nodes retrieve the data, verify its authenticity, and then transmit it back to the smart contract.

One of the key benefits of using Chainlink is that it provides a high level of security and reliability. The decentralized nature of the network means that there is no single point of failure, and the use of multiple oracle nodes ensures that the data is verifiable and trustworthy.

Using Chainlink to Trigger Complex Smart Contract Logic

Now that we have a basic understanding of what Chainlink is and how it works, let’s look at how we can use it to trigger complex smart contract logic.

There are two main ways to use Chainlink to trigger smart contract logic: using an external adapter or using a custom oracle contract. Let’s take a closer look at each of these methods.

Using an External Adapter

An external adapter is a piece of code that interacts with an external data source or event trigger and sends the data back to the smart contract. It acts as a bridge between the smart contract and the external world, allowing the smart contract to access external data and events.

To use an external adapter, you will need to first create a configuration file that specifies the details of the data request or event trigger. This includes the API endpoint, any relevant parameters, and the response data type. Here is an example configuration file for a data request to an external weather API:

{
    "name": "weather-adapter",
    "jobId": "weather-job",
    "url": "https://weather-api.com/forecast",
    "params": {
        "city": "${city}"
    },
    "resultPath": "$.temperature",
    "responseDataType": "uint256"
}

Once you have created the configuration file, you can use the Chainlink External Adapter to send a data request to the external data source. The External Adapter will retrieve the data and transmit it back to the smart contract, which can then trigger the desired logic based on the data received.

Using a Custom Oracle Contract

A custom oracle contract is a smart contract that acts as an oracle and retrieves data or triggers events on behalf of the main contract. It can be customized to fit your specific needs and can be used to trigger complex smart contract logic based on external data or events.

To use a custom oracle contract, you will need to create a contract that includes the necessary functions for sending data requests or event triggers to the external data source. You can then call these functions from your main contract to retrieve the data or trigger the event. Here is an example of a custom oracle contract that retrieves the current temperature for a given city:

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";

contract WeatherOracle {
    ChainlinkClient public chainlink;
    string public city;
    uint256 public temperature;

    constructor(ChainlinkClient _chainlink, string _city) public {
        chainlink = _chainlink;
        city = _city;
    }

    function requestTemperature() public {
        chainlink.requestData(chainlink.ORACLE_ADDRESS, jobId, city);
    }

    function onData(bytes32 _jobId, uint256 _temperature) public {
        require(_jobId == jobId, "Invalid job ID");
        temperature = _temperature;
    }

    function getTemperature() public view returns (uint256) {
        return temperature;
    }
}

Once you have created the custom oracle contract, you can call its functions from your main contract to retrieve the data or trigger the event. For example, you could call the requestTemperature() function from your main contract to retrieve the current temperature for the given city.

Customizing the Trigger Conditions and Parameters

One of the key benefits of using Chainlink to trigger complex smart contract logic is that you can customize the trigger conditions and parameters to fit your specific needs. This allows you to create highly flexible and adaptable smart contracts that can respond to a wide range of external data and events.

To customize the trigger conditions and parameters, you can modify the configuration file or the custom oracle contract to include the desired conditions and parameters. For example, you could specify a minimum temperature threshold that must be met before the smart contract logic is triggered, or you could include a list of cities that the contract should monitor for temperature changes.

Here is an example configuration file that includes a minimum temperature threshold:

{
    "name": "weather-adapter",
    "jobId": "weather-job",
    "url": "https://weather-api.com/forecast",
    "params": {
        "city": "${city}"
    },
    "resultPath": "$.temperature",
    "responseDataType": "uint256",
    "minThreshold": "-10"
}

And here is an example custom oracle contract that includes a list of cities to monitor:

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";

contract WeatherOracle {
    ChainlinkClient public chainlink;
    string[] public cities;
    mapping(string => uint256) public temperatures;

    constructor(ChainlinkClient _chainlink, string[] _cities) public {
        chainlink = _chainlink;
        cities = _cities;
    }

    function requestTemperatures() public {
        for (uint256 i = 0; i < cities.length; i++) {
            chainlink.requestData(chainlink.ORACLE_ADDRESS, jobId, cities[i]);
        }
    }

    function onData(bytes32 _jobId, string _city, uint256 _temperature) public {
        require(_jobId == jobId, "Invalid job ID");
        temperatures[_city] = _temperature;
    }

    function getTemperature(string _city) public view returns (uint256) {
        return temperatures[_city];
    }
}

By customizing the trigger conditions and parameters, you can create smart contracts that are highly responsive and adaptable to changing external conditions. This can be particularly useful for applications that need to respond to real-time data or events, such as supply chain management, financial applications, and more.

Conclusion

In this chapter, we learned how to use Chainlink to trigger complex smart contract logic based on external data or events. We also learned how to customize the trigger conditions and parameters to fit our specific needs. By the end of this chapter, you should have a solid understanding of how to use Chainlink to automate your smart contract logic and save time and resources.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Using the example configuration file provided in this article, create a configuration file that retrieves the current stock price for a given company. The stock price should be returned as a uint256 value.

{
    "name": "stock-adapter",
    "jobId": "stock-job",
    "url": "https://stock-api.com/price",
    "params": {
        "ticker": "${ticker}"
    },
    "resultPath": "$.price",
    "responseDataType": "uint256"
}

Using the example custom oracle contract provided in this article, create a custom oracle contract that retrieves the current temperature for multiple cities. The contract should have a function that allows the caller to request the temperatures for a given list of cities, and a function that returns the temperature for a given city.

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";

contract WeatherOracle {
    ChainlinkClient public chainlink;
    string[] public cities;
    mapping(string => uint256) public temperatures;

    constructor(ChainlinkClient _chainlink, string[] _cities) public {
        chainlink = _chainlink;
        cities = _cities;
    }

    function requestTemperatures() public {
        for (uint256 i = 0; i < cities.length; i++) {
            chainlink.requestData(chainlink.ORACLE_ADDRESS, jobId, cities[i]);
        }
    }

    function onData(bytes32 _jobId, string _city, uint256 _temperature) public {
        require(_jobId == jobId, "Invalid job ID");
        temperatures[_city] = _temperature;
    }

    function getTemperature(string _city) public view returns (uint256) {
        return temperatures[_city];
    }
}

Modify the example configuration file provided in this article to include a minimum temperature threshold. The smart contract logic should only be triggered if the temperature is below the threshold.

{
    "name": "weather-adapter",
    "jobId": "weather-job",
    "url": "https://weather-api.com/forecast",
    "params": {
        "city": "${city}"
    },
    "resultPath": "$.temperature",
    "responseDataType": "uint256",
    "minThreshold": "-10"
}

Modify the example custom oracle contract provided in this article to include a function that allows the caller to set the minimum temperature threshold. The contract should only return the temperature if it is below the threshold.

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";

contract WeatherOracle {
    ChainlinkClient public chainlink;
    string[] public cities;
    mapping(string => uint256) public temperatures;
    uint256 public minThreshold;

    constructor(ChainlinkClient _chainlink, string[] _cities, uint256 _minThreshold) public {
        chainlink = _chainlink;
        cities = _cities;
        minThreshold = _minThreshold;
    }

    function requestTemperatures() public {
        for (uint256 i = 0; i < cities.length; i++) {
            chainlink.requestData(chainlink.ORACLE_ADDRESS, jobId, cities[i]);
        }
    }

    function onData(bytes32 _jobId, string _city, uint256 _temperature) public {
        require(_jobId == jobId, "Invalid job ID");
        temperatures[_city] = _temperature;
    }

    function getTemperature(string _city) public view returns (uint256) {
        if (temperatures[_city] < minThreshold) {
            return temperatures[_city];
        }
    }

    function setMinThreshold(uint256 _minThreshold) public {
        minThreshold = _minThreshold;
    }
}

Using the example custom oracle contract provided in this article, create a custom oracle contract that retrieves the current weather conditions for a given city. The contract should have a function that allows the caller to request the weather conditions, and a function that returns the weather conditions as a string.

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.4/ChainlinkClient.sol";

contract WeatherOracle {
    ChainlinkClient public chainlink;
    string public city;
    string public conditions;

    constructor(ChainlinkClient _chainlink, string _city) public {
        chainlink = _chainlink;
        city = _city;
    }

    function requestConditions() public {
        chainlink.requestData(chainlink.ORACLE_ADDRESS, jobId, city);
    }

    function onData(bytes32 _jobId, string _conditions) public {
        require(_jobId == jobId, "Invalid job ID");
        conditions = _conditions;
    }

    function getConditions() public view returns (string) {
        return conditions;
    }
}