MatheusDaros / chainlink-intro

MIT License
7 stars 0 forks source link

Introduction

In this lesson, you will learn about two services that Chainlink provides for smart contract developers: Chainlink VRF and Chainlink Automations. These services enable smart contracts to access verifiable randomness and decentralized automation, which can enhance the functionality and security of your dApps.

Chainlink VRF

Chainlink VRF is a service that provides verifiable and unpredictable random numbers for smart contracts. It uses a cryptographic function that takes a secret key and a seed as inputs, and produces a random number and a proof as outputs. The proof can be verified by anyone using the public key, but the random number cannot be predicted or manipulated by anyone, not even the oracle operators or miners. This ensures that the randomness is fair and secure for applications that need it, such as gaming, NFTs, or lotteries.

You can learn more about Chainlink VRF from the official documentation.

How Chainlink VRF works

The Chainlink VRF works by allowing a smart contract to request a random number from a Chainlink node. The node operator running the Chainlink node will then use the VRF to generate a random number and provide it to the smart contract. The process involves the following steps:

image

Benefits of Chainlink VRF

Chainlink VRF offers several benefits for smart contract developers and users who need verifiable randomness:

Chainlink VRF Experimentation

To use Chainlink VRF inside a smart contract we first need to fund the LINK tokens needed to carry out this processing. There are two methods for requesting randomness with Chainlink VRF v2:

Direct funding method doesn't require a subscription and is optimal for one-off requests for randomness. This method also works best for applications where your end-users must pay the fees for VRF because the cost of the request is determined at request time.

Unlike the subscription method, the direct funding method does not require you to create subscriptions and pre-fund them. Instead, you must directly fund consuming contracts with LINK tokens before they request randomness. Because the consuming contract directly pays the LINK for the request, the cost is calculated during the request and not during the callback when the randomness is fulfilled. If you need help estimating the costs, check this documentation page.

Setting up the smart contract to call Chainlink VRF

To set up your consuming contract, you must take care of these steps:

You can learn more about how Chainlink process the request in this documentation page.

To test this process we're using this smart contract:

// SPDX-License-Identifier: MIT
// An example of a consumer contract that directly pays for each request.
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "@chainlink/contracts/src/v0.8/VRFV2WrapperConsumerBase.sol";

/**
 * Request testnet LINK and ETH here: https://faucets.chain.link/
 * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
 */

/**
 * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
 * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
 * DO NOT USE THIS CODE IN PRODUCTION.
 */

contract VRFv2DirectFundingConsumer is
    VRFV2WrapperConsumerBase,
    ConfirmedOwner
{
    event RequestSent(uint256 requestId, uint32 numWords);
    event RequestFulfilled(
        uint256 requestId,
        uint256[] randomWords,
        uint256 payment
    );

    struct RequestStatus {
        uint256 paid; // amount paid in link
        bool fulfilled; // whether the request has been successfully fulfilled
        uint256[] randomWords;
    }
    mapping(uint256 => RequestStatus)
        public s_requests; /* requestId --> requestStatus */

    // past requests Id.
    uint256[] public requestIds;
    uint256 public lastRequestId;

    // Depends on the number of requested values that you want sent to the
    // fulfillRandomWords() function. Test and adjust
    // this limit based on the network that you select, the size of the request,
    // and the processing of the callback request in the fulfillRandomWords()
    // function.
    uint32 callbackGasLimit = 100000;

    // The default is 3, but you can set this higher.
    uint16 requestConfirmations = 3;

    // For this example, retrieve 2 random values in one request.
    // Cannot exceed VRFV2Wrapper.getConfig().maxNumWords.
    uint32 numWords = 2;

    // Address LINK - hardcoded for Sepolia
    address linkAddress = 0x779877A7B0D9E8603169DdbD7836e478b4624789;

    // address WRAPPER - hardcoded for Sepolia
    address wrapperAddress = 0xab18414CD93297B0d12ac29E63Ca20f515b3DB46;

    constructor()
        ConfirmedOwner(msg.sender)
        VRFV2WrapperConsumerBase(linkAddress, wrapperAddress)
    {}

    function requestRandomWords()
        external
        onlyOwner
        returns (uint256 requestId)
    {
        requestId = requestRandomness(
            callbackGasLimit,
            requestConfirmations,
            numWords
        );
        s_requests[requestId] = RequestStatus({
            paid: VRF_V2_WRAPPER.calculateRequestPrice(callbackGasLimit),
            randomWords: new uint256[](0),
            fulfilled: false
        });
        requestIds.push(requestId);
        lastRequestId = requestId;
        emit RequestSent(requestId, numWords);
        return requestId;
    }

    function fulfillRandomWords(
        uint256 _requestId,
        uint256[] memory _randomWords
    ) internal override {
        require(s_requests[_requestId].paid > 0, "request not found");
        s_requests[_requestId].fulfilled = true;
        s_requests[_requestId].randomWords = _randomWords;
        emit RequestFulfilled(
            _requestId,
            _randomWords,
            s_requests[_requestId].paid
        );
    }

    function getRequestStatus(
        uint256 _requestId
    )
        external
        view
        returns (uint256 paid, bool fulfilled, uint256[] memory randomWords)
    {
        require(s_requests[_requestId].paid > 0, "request not found");
        RequestStatus memory request = s_requests[_requestId];
        return (request.paid, request.fulfilled, request.randomWords);
    }

    /**
     * Allow withdraw of Link tokens from the contract
     */
    function withdrawLink() public onlyOwner {
        LinkTokenInterface link = LinkTokenInterface(linkAddress);
        require(
            link.transfer(msg.sender, link.balanceOf(address(this))),
            "Unable to transfer"
        );
    }
}

For this contract to work, we're going to make sure that the consuming contract has enough LINK. To do so, complete these two steps:

1: Acquire testnet LINK;

2: Fund your contract with testnet LINK. For this example, funding your contract with 2 LINK should be sufficient.

Now that the contract is funded, we're going to test the randomness function with these steps:

To complete these steps using Remix, you can follow this tutorial.

Learn more about Chainlink VRF

(1) Introduction to Chainlink VRF | Chainlink Documentation: https://docs.chain.link/vrf/v2/introduction/

(2) What is Chainlink VRF, and how does it work? - Cointelegraph: https://cointelegraph.com/news/what-is-chainlink-vrf-and-how-does-it-work

(3) How Chainlink VRF works - Smart Contract Research Forum: https://www.smartcontractresearch.org/t/how-chainlink-vrf-works/2896

(4) Security Considerations: https://docs.chain.link/vrf/v2/security

Chainlink Automations

Chainlink Automations, formerly known as Keepers, is a service that enables smart contract developers to automate their contract functions in a decentralized manner. It uses a network of Chainlink nodes that monitor the contract state and trigger transactions when certain conditions are met. For example, a smart contract could use Chainlink Automations to automatically rebalance its portfolio, distribute rewards, or execute trades. Chainlink Automations saves developers time and resources, and provides reliable and scalable automation for their dApps.

You can learn more about Chainlink Automations from the official website.

How Chainlink Automations works

Chainlink Automations works by allowing a smart contract to register an upkeep with the Chainlink Automation Registry contract. An upkeep is a job or task that the smart contract wants to execute periodically or conditionally. The upkeep can be based on time (e.g., every hour) or custom logic (e.g., when the price of an asset reaches a certain level). The smart contract also needs to provide some payment in LINK tokens to fund the upkeep.

Chainlink Automation Animation

Benefits of Chainlink Automations

Chainlink Automations offers several benefits for smart contract developers and users who need decentralized automation:

Chainlink Automations Experimentation

There are two possible ways that Chainlink Automation will check when to execute smart contract functions:

Setting up the smart contract to use Chainlink Automations

To use Chainlink Automation, contracts must meet the following requirements:

After you deploy your contract, you'll need to setup the automation process in the Automation UI.

To test this process we're using this smart contract:

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

// AutomationCompatible.sol imports the functions from both ./AutomationBase.sol and
// ./interfaces/AutomationCompatibleInterface.sol
import "@chainlink/contracts/src/v0.8/AutomationCompatible.sol";

/**
 * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
 * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
 * DO NOT USE THIS CODE IN PRODUCTION.
 */

contract Counter is AutomationCompatibleInterface {
    /**
     * Public counter variable
     */
    uint public counter;

    /**
     * Use an interval in seconds and a timestamp to slow execution of Upkeep
     */
    uint public immutable interval;
    uint public lastTimeStamp;

    constructor(uint updateInterval) {
        interval = updateInterval;
        lastTimeStamp = block.timestamp;

        counter = 0;
    }

    function checkUpkeep(
        bytes calldata /* checkData */
    )
        external
        view
        override
        returns (bool upkeepNeeded, bytes memory /* performData */)
    {
        upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
        // We don't use the checkData in this example. The checkData is defined when the Upkeep was registered.
    }

    function performUpkeep(bytes calldata /* performData */) external override {
        //We highly recommend revalidating the upkeep in the performUpkeep function
        if ((block.timestamp - lastTimeStamp) > interval) {
            lastTimeStamp = block.timestamp;
            counter = counter + 1;
        }
        // We don't use the performData in this example. The performData is generated by the Automation Node's call to your checkUpkeep function
    }
}

For this contract to work, we're going to register it in the Automation UI

After you set up this Upkeep, you can view the Upkeep details at the Automation UI and also by calling the smart contract functions:

To complete these steps using Remix, you can follow this tutorial

Learn more about Chainlink Automations

(1) Introduction to Chainlink Automation | Chainlink Documentation: https://docs.chain.link/chainlink-automation/introduction/

(2) Chainlink Automation Architecture | Chainlink Documentation: https://docs.chain.link/chainlink-automation/overview/

(3) Reliable, high-performance smart contract automation: https://chain.link/automation

(4) Chainlink Automation Best Practices: https://docs.chain.link/chainlink-automation/compatible-contract-best-practice