---
# System prepended metadata

title: 利用chainlink建立dNFT

---

# 利用chainlink建立dNFT



# 什麼是dNFT?

dNFT是指會在特定條件下不斷變化的NFT，相較一般的NFT在鑄造後，URI就是固定的，且背後的URI通常連接IPFS，意即其內容無法變更

而dNFT則是我們會在合約內編寫某個條件或規則，一旦這個條件成立，就會觸發某些功能，例如改變dNFT的URI，或其中的某些數值。

然而在實作上，如何去觸發是一個問題，手動是一個可行但不夠聰明的辦法，較為可行的方式是建立一個server，讓這個server去定期監測contract的條件有沒有成立，再決定要不要觸發，但是這樣的問題就是function只能由server那邊做觸發，整個架構就會趨於中心化。

於是目前較為普遍的做法是採用chainlink幫忙完成觸發，chainlink是一個oracle，負責鏈上與鏈下的資料交互，而這種可以讓chainlink幫忙觸發的功能又被稱為**Automation**

在製作dNFT的時候，我們可以選擇兩種觸發方式，**一種是Time-based Automation**，另一種則是**Custom Logic Automation** 

## 前置準備

此實作需要先行部署欲觸發的合約，我這邊都用Remix做舉例，因此需要對Remix有過使用經驗

我們需要先產生一個Upkeep，每個Upkeep來負責觸發一個合約

接下來的操作都是在sepolia testnet上進行，請確定自己在做以下操作是不是都在正確的鏈上

1. 先前往[Chainlink Faucet](https://faucets.chain.link/) 領取測試幣
> 這邊會要求你連接推特才能領取測試幣
2. 如果成功的話你的錢包應該會有0.1test ETH跟20 test LINK，接著再前往[Chainlink Automation](https://automation.chain.link/)
> Metamask 需要新增LINK token才能看到你擁有的LINK數量，Sepolia LINK address: 0x779877A7B0D9E8603169DdbD7836e478b4624789，如果在faucet領取上遇到問題可以查看官方頁面 [Acquire testnet LINK](https://docs.chain.link/resources/acquire-link)
3. 連接右上角連接錢包
4. 點選**Register new Upkeep** 來產生一個新的Upkeep

## Time-based

Time-based的Upkeep可以設定固定的觸發時間，例如每三分鐘就固定觸發合約中的某一function

在開始之前**請先部署**你要觸發的合約，這邊提供範例，你可以將其複製到Remix，並部署上Sepolia

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;


contract Counter {
    uint public counter;

    constructor(){
        counter = 0;
    }

    function count() external {
        counter = counter + 1;
    }

}
```

1. 進入Register new Upkeep後，選擇Time-based
![](https://i.imgur.com/GSeNALr.png)

2. 輸入你想要觸發的contract address，點選Next
> 這個contract必須先部署在Sepolia上
3. 輸入contract的ABI(若你是從Remix上部署的，可以去Compile介面底下複製ABI)
4. 選擇你想要觸發的function，若你是使用上方的範例合約的話，我們這邊選擇`count()`這個function
5. 輸入你想要觸發的時間，這邊用CRON表示法輸入，或者你可以選用底下的模板
6. 輸入Upkeep的細節設定，這邊可以設定起始資金、GasLimit、Upkeep name等等
> 起始資金這邊可以多放一些，以免造成Upkeep資金過少無法觸發的狀況
7. 點選**Register Upkeep**按鈕
8. 完成

![](https://i.imgur.com/zsgHAV9.png)

如果成功的話在Upkeep頁面底下的History可以看到Activity type為**Perform Upkeep**的transaction被送出(查看圖片左邊的時間，可以發現我是設定3分鐘觸發一次)，並且合約的`counter`不斷地加一

## Custom Logic

Custom Logic的觸發方式為，讓用戶去設定**滿足某個條件的時候才觸發合約**的function，但這邊要特別注意的地方是，這個合約必須是**Compatible Contracts**(具有兼容性的合約)，講白話一點就是這個被觸發的這個合約必須遵守他們的某些規範，才可以被正常運作

這個規範就是，合約必須繼承`AutomationCompatibleInterface`，藉此確保合約內有`checkUpkeep`跟`performUpkeep`兩個function

`checkUpkeep`是一個view function，目的是用來判斷條件是否達成，如果條件成立就return true，因此用戶可以在這個function裡頭編寫自己的條件，而chainlink那邊會在每個block都去調用`checkUpkeep`，如果回傳的結果是true的話，就執行`performUpkeep`

`performUpkeep`這個function裡頭則編寫你想要去觸發的動作

下面是官方提供的範例(可以在[Creating Compatible Contracts](https://docs.chain.link/chainlink-automation/compatible-contracts/)文件中點選Open in Remix來快速開啟跟方便後續部署)

```solidity
// 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
    }
}

```

可以看到這個合約的`checkUpkeep`，是去確定現在的timestamp距離上次儲存的timestamp是不是已經超過我們所設定的interval了，並把比較的結果return出來

如果`checkUpkeep`return true，那麼就執行`performUpkeep`，這個範例合約`performUpkeep`做的事情是將`lastTimeStamp`設為現在的timestamp，並將`counter`加一

也就是說如果`interval`設定為60的話，那整個功能就是**每經過60秒`counter`就會加一**

這邊如果要編寫更為複雜的功能的話，建議閱讀官方文檔[Creating Compatible Contracts
](https://docs.chain.link/chainlink-automation/compatible-contracts/)

#### 接下來是去創建一個Custom Logic的Upkeep，整體流程跟Time-based相似

1. 進入Register new Upkeep後，選擇Custom Logic
2. 輸入你想要觸發的contract，點選Next，這邊會去驗證你的contract有無兼容
![](https://i.imgur.com/vk8dK4u.png)
3. 輸入Upkeep的細節設定，這邊可以設定起始資金、GasLimit、Upkeep name等等
4. 點選**Register Upkeep**按鈕
5. 完成

![](https://i.imgur.com/7HfWKzJ.png)

我部署的是上面官方提供的範例合約，我將`interval`設定為60，所以這邊可以看到他60秒發送一個`Perform Upkeep`的transaction

## 備註

這篇文章僅提供初步的認識跟練習，若要編寫正式的合約建議還是閱讀官方文檔，若上述製作有任何問題可以詢問我，有其他寫錯的地方也歡迎指正


## Reference
[Chainlink Docs](https://docs.chain.link/chainlink-automation/introduction/)
[【DAY15】 - Dynamic NFT & Oracle (Ｉ)](https://ithelp.ithome.com.tw/m/articles/10299344)