iT邦幫忙

2022 iThome 鐵人賽

DAY 14
1
Web 3

Road Map To DApp Developer系列 第 14

【DAY14】 - Fetch Data From Opensea

  • 分享至 

  • xImage
  •  

Preface

今天要做的是透過 Opensea 取得 ticket 的圖片、擁有者等資訊,並將它們顯示在頁面中。

How to fetch Data?

一般而言要從外部取得資料,是需要透過 API (Application Programming Interface) 來取得的。如果把 API 比喻成實體的物體,他就像一個連接應用程式之間的橋梁。就像是 ERC721 等協議,他們也屬於一種 API,只要是應用程式之間存在同一種協議,他們就可以透過 API 來取得各種相同格式的資料。

sited from astera.com

Steps

以下是我想要開發的頁面流程:

  1. 設計一個 Verify 頁面,透過 <Route> 將其變成一個分頁。
  2. 在 Verify 頁面中,可以透過 useFetch() 我們自定義的函式以取得 ticket 的資訊。
  3. 在取得資料之前,頁面要顯示「Loading...」。
  4. 從 Opensea 取得資料。
  5. 得到資訊後可以顯示使用者擁有的 ticket 數量與其 image。

實作

雖然這個步驟只有短短的幾步,但是花費了我好多好多的時間?。

1. 設計 Verify 頁面

首先這部非常簡單,就是在 src 中建立 Verify.js,並與昨天一樣,新增一個包住 <Verify /><Route> 並將其 path='/Verify'

另外我將原本左上角的選單(Menu)改成了三個 <Link/>

由左至右分別是 HomeMintVerify 三個頁面(其實在昨天的頁面中就可以看到了XD)。

2. 透過 useFetch() 取得資料

在 React 中 fetch 別的地方的資料,需要使用 fetch 這個 function,但是直接使用 fetch 寫在 Verify.js 中似乎有點龐雜也不模組化,因此我沿用之前學習的資源,使用自定義的 Hook function,順便練習 useEffect() 的使用。

import { useState, useEffect } from "react";

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [isPending, setIsPending] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const abortCont = new AbortController();
    
    setTimeout(() => { 
      fetch(url, { signal: abortCont.signal })
      .then(res => {
        if (!res.ok) { // error coming back from server
          throw Error('could not fetch the data for that resource');
        } 
        return res.json();
      })
      .then(data => {
        setData(data); 
        setIsPending(false);
        setError(null);
      })
      // send error
      .catch(err => {
        if (err.name === 'AbortError') {
          console.log('fetch aborted');
        }else {
          setIsPending(false);
          setError(err.message);
        }
      })
    }, 4000);
    return () => abortCont.abort();
  }, [url]);

  return { data, isPending, error };
}

export default useFetch;

data, isPending, error

這三個東西主要是拿來接住一些訊息的:

  • data: 接住最重要的 NFT 資訊。
  • isPending: 當接收到訊息的時候會呈現 false,與之後頁面是否呈現 Loading 有關。
  • error: 如果發生錯誤需要接收。

abortController

fetch 中,由於請求沒有辦法被取消,因此需要使用 abort() 來取消請求。

其邏輯是當 fetch 超過 setTimeout() 的時間時,會呼叫 abortController.abort() 再透過 signal = {signal} 傳送進去 fetch() 中取消請求。

3. Opensea endpoint

而我們要從哪裡獲得 ticket 的資料呢?

Opensea 提供了開發者一些 end point,讓開發者可以透過 GET 的 HTTP method 來獲得來自 Opensea 的資料。

HTTP 是一個底層的網路協議,HTTP method 制定了資料的使用方式(例如查詢、修改、新增等),而在其中的 GET 就像是查詢存在於 url 中的資料。

從 Opensea 的 Doc 中可以找到 Retrieve an asset (Testnets) 的頁面,填入我們的合約地址並在 tokenId 輸入 3(因為目前只有 audience)。可以從旁邊的 try it! 可以找到可以使用的 api end point:

https://try.readme.io/https://testnets-api.opensea.io/api/v1/asset/0x4D2669542f0e6041C83c83cbEDa8df6262977Ec1/3/?account_address=0xaB50035F25F96dd3DEf1A7a9cC4E1C81AD6a7651

接下來就可以在 useFetch() 中傳入這個 url,便可以得到像這樣的資料:

{
    id: 130756285, 
    num_sales: 0, 
    background_color: null, 
    image_url: 'https://lh3.googleusercontent.com/YPoAgmuHxay-uQyy…q7AJy0Jlw_yFUrljg3HnuJ2agMNJOit1qCuOHQFUYc5EOk85g', 
    image_preview_url: 'https://lh3.googleusercontent.com/YPoAgmuHxay-uQyy…0Jlw_yFUrljg3HnuJ2agMNJOit1qCuOHQFUYc5EOk85g=s250',
    ...more
}

事實上我們想要的資料就只有:top_ownerships 這個 key 裡面的 owner.address(所有擁有這種票的地址) 和 quantity(這個 owner 共有幾張票),還有 image_original_url 而已。

4. 顯示 Loading

這邊使用 isPending 這個 boolean 來控制各種樣式的出現與否。

{ 
    !isPending && userOwnsTickets && 
    <div className="nft-box">
        <img src={image} alt="image" className="nft-img"/>
        <div className="nft-num">You have { ticketNum } tickets</div>
    </div>
}
{ isPending && <div className="pending">Loading ...</div> }
{ !isPending && !userOwnsTickets && <div className="no-ticket"> You don't own any ticket yet! </div>}

5. display

useEffect(() => {
  if (nftData !== null) {
    const ownerships = nftData.top_ownerships;

    for (const ownership of ownerships) {
      if (accounts[0] === ownership.owner.address) {
        setOwnsTicket(true);
        setTicketNum(ownership.quantity)
        setImage(nftData.image_original_url);       
      }
    }
  }
});

這邊單純的就是把剛剛提到我們需要的資訊用變數接住!

並最後在頁面中顯示:

<div className="nft-box">
    <img src={image} alt="image" className="nft-img"/>
    <div className="nft-num">You have { ticketNum } tickets</div>
</div>

測試

這邊用了一個原本只有 4 張 ticket 的帳號, mint 了兩張 ticket,最後成功的 fetch 到了增加後的數量。

(可以發現我在測試的時候用了 Goerli ETH,主要是因為 Opensea 不支援在 Polygon 上 fetch 資料的原因,因此之後的開發鏈也會使用 Goerli 來測試)

Closing

這篇的內容看似簡單,但實際上花了我十幾個小時來實作。從我一開始想要使用 opensea-js 來獲得資料,發現 opensea-js 安裝在我的專案中會產生很多的 error,礙於時間問題而打消使用他的念頭,轉而使用 NFT-API 或 Moralis 等 api,但最後仍無功而返。最後終於找到一個非常非常簡單的 fetch 方式!

但是上述提到的內容我在未來也需要學會,希望可以早日查詢到那些 error 並用他們開發。(或是看看網路上影片中是如何實作的)

今天後會進入另一個階段:NFT other functions,主要是與 NFT 的相關的功能與變形!

References


若有文章內有任何錯誤的地方歡迎指點與討論!非常感謝!

歡迎贊助窮困潦倒大學生
0xd8538ea74825080c0c80B9B175f57e91Ff885Cb4


上一篇
【DAY13】 - Mint button & Connect With Smart Contract
下一篇
【DAY15】 - Dynamic NFT & Oracle (I)
系列文
Road Map To DApp Developer30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言