lesson-2_自動実行を実装しよう
🐣 自動実行を実装しましょう
chainlinkの自動化には以下の2つの起動方法があります。
Time-based trigger
: あらかじめ指定した時間でコントラクトを実行します。定期実行です。Custom logic trigger
: あらかじめ用意した条件に合致した場合にコントラクトを実行します。
今回は、 「有効期限が切れたfarmNft
がないかどうかの確認、 あった場合は削除処理をする」のでCustom logic trigger
を使用します。
Custom logic trigger
の実装
ここで行うことは、 Custom logic trigger
に必要な実装をAssetTokenization
コントラクトに実装します。
そしてchainlinkにCustom logic trigger
で実行してもらう旨をタスクとして登録します。
このタスクのことをUpkeep
と呼びます。
AssetTokenization.sol
の中に以下のimport文を追加し、 さらにAutomationCompatibleInterface
を継承するようにしてください。
※ 継承を記述した時点ではコードエディタによりAutomationCompatibleInterface
を実装できていない警告が出るかもしれませんが、 この時点では無視して構いません。
import "@chainlink/contracts/src/v0.8/AutomationCompatible.sol";
contract AssetTokenization is AutomationCompatibleInterface {
...
AutomationCompatibleInterface
はchainlinkが用意したインタフェースで、 これを実装することによりUpkeepはどの条件を確認し、 何を実行するのか判別することができます。
次にAssetTokenization
の最後の行に以下の関数を貼り付けてください。
// For upkeep that chainlink automation function.
// Check whether there are expired contracts.
// If checkUpkeep() returns true, chainlink automatically runs performUpkeep() that follows below.
function checkUpkeep(
bytes calldata /* optional data. don't use in this code */
)
external
view
override
returns (
bool upkeepNeeded,
bytes memory /* optional data. return initial value in this code */
)
{
for (uint256 index = 0; index < _farmers.length; index++) {
address farmer = _farmers[index];
if (!availableContract(farmer)) {
continue;
}
if (_farmerToNftContract[farmer].isExpired()) {
return (true, "");
}
}
return (false, "");
}
// For chainlink.
// Burn expired NFT and delete NFT Contract.
function performUpkeep(
bytes calldata /* optional data. don't use in this code */
) external override {
for (uint256 index = 0; index < _farmers.length; index++) {
address farmer = _farmers[index];
if (!availableContract(farmer)) {
continue;
}
if (_farmerToNftContract[farmer].isExpired()) {
_farmerToNftContract[farmer].burnNFT();
delete _farmerToNftContract[farmer];
}
}
}
checkUpkeep
とperformUpkeep
をによりAutomationCompatibleInterface
を実装することができました。
checkUpkeep
は条件に合致したかどうかの論理値を返却します。
今回は期限切れのfarmNft
があるかどうかを条件とし、 ある場合はtrue
を返却します。
performUpkeep
はcheckUpkeep
がtrue
を返却した場合に何を実行するのかを記述します。
今回は期限切れのfarmNft
に対してburnNFT
を実行し、 マッピングからdeleteします。
🧪 テストを追加しましょう
AssetTokenization.ts
のimport文のところにtimeを追加してください。
import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers";
次に、テストの最後の行に以下のコードを貼り付けてください。 ※ この時点ではコードエディタにより実装していない関数を実行しているという警告が出るかもしれませんが無視して構いません。
describe("upkeep", function () {
it("checkUpkeep and performUpkeep", async function () {
const { userAccounts, assetTokenization } = await loadFixture(
deployContract
);
// 定数用意
const farmer = userAccounts[0];
const farmerName = "farmer";
const description = "description";
const totalMint = BigNumber.from(5);
const price = BigNumber.from(100);
const expirationDate = BigNumber.from(Date.now())
.div(1000) // in second
.add(oneWeekInSecond); // one week later
// nftコントラクトをデプロイ
await assetTokenization
.connect(farmer)
.generateNftContract(
farmerName,
description,
totalMint,
price,
expirationDate
);
const [return1] = await assetTokenization.checkUpkeep("0x00");
// この時点では期限切れのnftコントラクトがないのでfalse
expect(return1).to.equal(false);
// ブロックチェーンのタイムスタンプを変更(期限の1s後のタイムスタンプを含んだブロックを生成)し、 nftコントラクトの期限が切れるようにします。
await time.increaseTo(expirationDate.add(1));
const [return2] = await assetTokenization.checkUpkeep("0x00");
// 期限切れのnftコントラクトがあるのでtrue
expect(return2).to.equal(true);
await assetTokenization.performUpkeep("0x00");
// 期限切れのnftコントラクトの情報は取得できない
await expect(assetTokenization.getNftContractDetails(farmer.address)).to.be
.reverted;
});
});
新しく実装したcheckUpkeep
とperformUpkeep
について正しく動作しているかを確認しています。
⭐ テストを実行しましょう
以下のコマンドを実行してください。
⚠️ 追加したテストコードではテストヘルパーパッケージのtimeを使用しています。 timeの使用はテスト環境全体の時間に影響するため、 複数のテストファイルを同時にテストすると予期せぬ挙動を起こす場合があります。よって以下のコマンドではテストをする対象ファイルを引数によって指定しています。
yarn test
以下のような表示がされたらテスト成功です!