プログラミングのゴミ箱

日々の学習の中で知らなかったことについて、調べたことを解説します。

ChainLinkを使ってIPFSからデータを取ってきた

ChainLinkを使ってIPFSからデータを引っ張ってきたときの備忘録。

Pinataを使ってデータをIPFSに登録

Pinataを使ってこのデータをIPFS上に保存。

{
  "HP": 100
}

実際にデータを撮ってみる

Remixを使って開発していきます。まずは、ライブラリのインポート。

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

つぎに、これらのコードを継承。

contract myContract is 
ChainlinkClient, 
ConfirmedOwner {
    using Chainlink for Chainlink.Request;

    constructor() ConfirmedOwner(msg.sender) {
        setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
        setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
    }
}

setChainlinkTokenのところには$LINKトークンのアドレスを、setChainlinkOracleにはどのチェーンを使用するか選択します(今回はgoerliネット)。
詳しくは以下を参照。
Testnet Oracles | Chainlink Documentation

次に、実際にデータを取得するコードを書いてみる。

    function requestHP() public returns (bytes32 requestId) {
        Chainlink.Request memory req = buildChainlinkRequest("ca98366cc7314957b8c012c72f05aeeb", address(this), this.fullfill.selector);
        req.add("get", "https://gateway.pinata.cloud/ipfs/QmdctF9rpwsTRnZHScFhu5m2ss1VoFKdtfsgVskpLbNwDp");
        req.add("path", "HP");
        req.addInt("times", 1);
        return sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10);
    }

    function fullfill(bytes32 _requestId, uint256 _HP) public recordChainlinkFulfillment(_requestId) {
        HP = _HP;
    }

    function getHP() public view returns (uint256) {
        return HP;
    }

これで、requestHP関数を呼んだあとにgetHP関数を呼び出してデータを取得することができるようになりました。
一個ずつ解説。

  • buildChainlinkRequest

第一引数に「jobId」を指定する。jobIdというのはChainlinkでどのようなデータ型を扱いたいかを指定するためのID。今回はuint256型を取得するためのIDを指定した。
他の型についても、先程のリンクに書いてある。
Testnet Oracles | Chainlink Documentation

第二引数にはこのコントラクトのアドレスを、第三引数にはコールバック関数(値が取得できた場合に呼び出される関数)を指定する。

  • req.add("get", ...)

getリクエストを実行するということを宣言。

  • req.add("path", "HP")

取得したJsonデータのうちのどこにあるデータを使用するか指定。
例えば、以下のようなJsonが返ってきた場合を例に考えると

[
    {
        "greeting": "good mornin"
    },
   {
        "greeting": "good night"
   }
]

この場合は
req.add("path", "0, greeting");
とすることで取得できる。
今回はHPが取得したいのでこのような形となる。

  • req.addInt("times", 1)

これは、数値を受け取る場合のみに必要なプロパティである。受け取った数字を何倍かして返したい場合に指定する(何もかけない場合は1を指定)。
10 ** 18 などがよく指定される。

  • sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10)

第二引数には支払う$LINKの量が指定される。今回は0.1Link支払っている。

実行

実際にデータを取得してみる。
デプロイボタンを押して、requestHP関数を呼び出せばよいだけだが一つ注意点として、$LINKをデプロイしたコントラクト宛に送ってやらないと関数の呼び出しが失敗してしまう。
(僕はこれに気がつくのに1時間かかりました、、、)

requestHPを呼び出した後にgetHPを呼び、100という数字が返ってきたら成功!!おつかれ!!!

全体のコード

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

contract myContract is 
ChainlinkClient, 
ConfirmedOwner {
    using Chainlink for Chainlink.Request;

    constructor() ConfirmedOwner(msg.sender) {
        setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
        setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
    }

    function requestHP() public returns (bytes32 requestId) {
        Chainlink.Request memory req = buildChainlinkRequest("ca98366cc7314957b8c012c72f05aeeb", address(this), this.fullfill.selector);
        req.add("get", "https://gateway.pinata.cloud/ipfs/QmdctF9rpwsTRnZHScFhu5m2ss1VoFKdtfsgVskpLbNwDp");
        req.add("path", "HP");
        req.addInt("times", 1);
        return sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10);
    }

    function fullfill(bytes32 _requestId, uint256 _HP) public recordChainlinkFulfillment(_requestId) {
        HP = _HP;
    }

    function getHP() public view returns (uint256) {
        return HP;
    }