原文鏈接:NFT 及 OpenSea 交易背后的技术分享

作者:Xing

Web3Caff 經作者授權發佈

這段時間 NFT 市場火爆,本來準備與大家分享的去中心化存儲項目改為了這一篇關於 NFT 及 OpenSea 的學習分享了。

最近購入了 NFT 的各位可以看看你花大價錢購買的 NFT 的背後究竟是個什麽東西,OpenSea 是如何實現對 NFT 進行買賣交易的,這篇文章可以作為一個入門了解。也許這篇文章還是你成為幣圈所謂「科學家」的啟蒙教程。

從本人第一次購買 NFT 的故事講起

鄙人在今年 1 月 3 日的時候,從推特上看見有消息說周傑倫發行了他的 NFT PhantaBear,所以本著粉絲的心態去了解一下。當時發現小熊的地板價只有 0.4ETH,所以立即花了 2ETH 購入 4 只小熊。(後來被 @BTCdayu 傳授經驗應該買稀有屬性的,所以之後又購入了兩只稀有小熊)

之後兩天鄙人在推特上看見很多人說 PhantaBear 因為周董發表申明切割,所以項目不值得投資,故本人在推特上發表了自己的觀點力挺 PhantaBear,同時公開打賭 PhantaBear 一年之後如果年化收益不到 300% 的話(2E,打賭時小熊地板價在 0.6E 多點),願意贈送一只小熊。結果沒想到才過了兩三天我自己打了自己的臉,關於我的觀點與公開賭註大家可以參考這條推特

總的來說就是在用戶心智中能夠占據第一的項目,都是比較好的標的。比特幣是整個區塊鏈行業的開創者,所以是最值得投資的標的;以太坊占據了擁有智能合約功能的第二代區塊鏈中的第一名,所以也是值得投資的(雖然我也沒有持倉);CryptoPunks 是第一個 NFT 項目,所以也很值得投資。(18 年的時候有個加密貓遊戲,我印象中那個好像是 NFT 始祖,但貌似現在沒人提了?)

而 PhantaBear 是華人圈的第一個 NFT 項目,同時又是伴隨我成長的天王帶貨的,所以無論是理性上還是感性上都必須拿下。

很多時候自己在 crypto 行業中的投資,更看中的是這個項目背後的故事及其意義,而一些 utility token 才會去分析是否有用,是否解決了行業的痛點或者問題。

自購入了人生中的第一個 NFT 之後,上周整周時間都在刷推特、混小熊的 discord(修煉升級、偶爾會有明星聊天)、刷 floor price,把原本安排寫作的時間都浪費掉了。

所以這周開始摒棄各種刷 social network,學習研究 OpenSea 平臺上交易 NFT 的技術細節並記錄分享,畢竟熱鬧繁華過後還是需要積累沈澱一些東西。

這篇文章鄙人會先講一講 NFT 的合約(ERC721)以及 NFT 在 OpenSea 上是如何實現交易的,這兩部分的內容比較偏技術,希望可以耐心看完。

前面廢話太多,下面正式開始。

NFT 與 ERC721

NFT 的名詞定義就不說了,網上有很多資料大家可以自己搜索。

對於 NFT 來說,實際上就是 Ethereum 協會定義的一個規範,也就是 ERC721,其作用是與 Fungible Token 的 ERC20 規範一樣,通過這個規範來統一接口,使得在 Token 或者 NFT 上可以衍生出各種各樣的 DAPP 生態,比如各種 Swap、借貸等等各類 DAPP。

上面說的可能有點抽象,打個廣告舉個例子:鄙人為了學習寫合約,練手擼了一個去中心化的紅包 DAPP,實際上就是把微信紅包這個產品挪到了區塊鏈上,不同的是紅包 DAPP 塞進紅包裏的是各種 Token。這個紅包 DAPP 之所以支持所有 Token,正是因為適配了所有 Token 都遵循的 ERC20 規範。

NFT 的 ERC721 規範是從 ERC20 衍生出來了,有很多相同方法,其下規範方法如下:

// 查詢 NFT 中某個 owner 擁有的數量
balanceOf(owner)

// 查詢 NFT 中某個編號屬於的是哪個人,例如查詢某個編號的猴屬於哪個 owner
ownerOf(tokenId)

// 返回 NFT 的名字
name()

// 返回 NFT 的符號
symbol()

// NFT 總發行量
totalSupply()

// 返回某個 NFT 的 URI,這個 URI 就是這個 NFT 的一切描述信息
tokenURI(tokenId)

// 按 index 序號返回該 owner 的所有持有 NFT 的編號
tokenOfOwnerByIndex(owner, index)

// 按 index 序號返回 NFT 的編號
tokenByIndex(index)

// 允許 to 這個地址可以轉移他的 tokenId 編號的 NFT(攸關你的 NFT 安全!)
approve(to, tokenId)

// 查詢 tokenId 編號的 NFT 授權給了誰(查詢誰可以轉走你的 NFT!)
getApproved(tokenId)

// 授權或者取消授權 operator 這個地址轉移你這一 Collection 下的所有 NFT(攸關你的 NFT 安全!)
setApprovalForAll(operator, approved)

// 查詢某個 operator 是否有權轉移某個 owner 的這一 Collection 中的所有 NFT(查詢某個地址是否可以轉走你這個 Collection 的所有 NFT!)
isApprovedForAll(owner, operator)

// 將 from 這個地址的 tokenID 編號的 NFT 轉給 to 這個地址(需要授權才行)
transferFrom(from, to, tokenId)

以上就是幾個主要的 ERC721 規範的方法,雖然寫了註釋,但是我猜測大家還是一臉懵。

所以下面給大家通過例子來說明,從 NFT 的起源 Mint 開始。

Mint 鑄造

下面這段代碼就是 OpenZeppelin 上一個簡單的如何鑄造 NFT 的代碼:

// 鑄造 NFT 的方法,只需要傳入鑄造給誰,再加上這個 NFT 的 tokenURI 即可
function awardItem(address player, string memory tokenURI)
    public
    returns (uint256)
{
    _tokenIds.increment();
    uint256 newTokenId = _tokenIds.current();

    // 將這個新的 tokenId 與 player 綁定
    _mint(player, newTokenId);

    // 將 tokenURI 與這個 newTokenId 綁定
    _setTokenURI(newTokenId, tokenURI);

    return newItemId;
}

鑄造 NFT 實際上就是往 NFT 的合約裏寫入了兩個信息:

  • tokenId 及其 owner
  • tokenId 及其 tokenURI

有人說 NFT 又有圖片,又有各種屬性,怎麽這麽簡單了就鑄造出來了呢?

沒錯,就這麽簡單!

接下來我們用 CryptoApes 這個 NFT 舉例給大家看看我們大家看到的各種眼花繚亂的 NFT 是怎麽展示出來的?

Play with NFT contract

我們先來看看如何通過簡單的幾行代碼查詢 CryptoApes NFT 的基本信息:

截圖中可以看到,通過向 CryptoApes 合約調用上述 ERC721 規範的方法 name(),symbol(),totalSupply(),就能拿到這個 NFT 的名稱、符號和總數分別是:CryptoApes,CRAP,6969。

CryptoApes 的合約地址以及 NFT 編號可以在 OpenSea 中的 URL 拿到,見下面截圖:

可以看到 OpenSea 的 NFT 鏈接 https://opensea.io/assets/0x29714cafe792ef8b8c649451d13c89e21a0d7f5b/24 ,assets 後的第一個地址就是該 NFT Collection 的合約地址,合約地址後的數字就是該 NFT 編號

下面我們還是通過幾行簡單代碼調用合約,查詢一下這只編號為 24 的 CryptoApes 的信息:

可以看到我們通過 ERC721 規範中的 ownerOf()方法,就能查到這只 CryptoApes 的擁有者(沒錯,就是本人)。

另外通過 balanceOf()方法就能查到本人總共擁有了 4 只 CryptoApes。

值得註意的是截圖中的 tokenURI()方法,通過它就能獲取這個 24 號 NFT 的 tokenURI。這個 tokenURI 非常重要!因為裏面存儲了關於這只猴的所有描述信息(Metadata)。

24 號猴的 tokenURI 是:ipfs://QmWGAFtzyzB6A6gYMnb6838hysHuT2rcV8B98Gmj4T4pyY/24.json,說明是存儲在 IPFS 這個分布式存儲上的 json 文件,我們繼續通過這個 IPFS 的網關地址https://ipfs.io/ipfs/QmWGAFtzyzB6A6gYMnb6838hysHuT2rcV8B98Gmj4T4pyY/24.json)訪問獲取裏面的內容:

上面截圖中就是這只 24 號猴的所有信息了,包括名字、圖片、描述、編號、創建時間、還有背景顏色、毛顏色、嘴巴衣服等各種各樣的屬性。(大家點開大圖就能看見)

其中的圖片信息又指向了另外一個 IPFS 地址:ipfs://QmZFnUm3bjSyEPrvxEa3fR9eUxnkfQeLmPTzDhAmCWtbMZ/24.png,通過這個地址https://ipfs.io/ipfs/QmZFnUm3bjSyEPrvxEa3fR9eUxnkfQeLmPTzDhAmCWtbMZ/24.png)就能看到 24 號猴的圖片了,這也是 OpenSea 平臺中給大家展示的圖片樣子。

大家可以看到 NFT 的所有關鍵信息都是存在 tokenURI 這個鏈接中的,這個鏈接中的數據包括了你 NFT 的編號、屬性、圖片或視頻,所以 tokenURI 這個鏈下數據能不能正常訪問,會不會被篡改非常重要!!!

這裏 CryptoApes 的 TokenURI 用的是 IPFS 協議,IPFS 協議可以保證不會被篡改。但如果是 HTTP 協議則有被篡改的可能,雖然使用 HTTP 協議可以通過 checksum 校驗數據等方式驗證,但這並不優雅,並且也不是 ERC721 的協議規範。

這裏插一句,鄙人原本打算寫一篇介紹去中心化存儲的文章的,因為感覺這個領域的重要性被大家嚴重忽視了,它也是區塊鏈生態的基礎設施,重要性並不比被熱捧的各類公鏈項目低。使用 IPFS 協議存儲內容雖然不會被篡改,但是 IPFS 還存在它自身的問題,這裏就不展開了。

接著說,上面我們查到了編號 24 的猴的所有關鍵信息。那如果想把所有 6969 只 CryptoApes 的信息查找出來可以嗎?

當然可以!使用上述 ERC721 規範中的「tokenByIndex()」方法進行遍歷就行,有興趣的朋友可以自行試試。

實際上 OpenSea 支持所有的 ERC721 的 NFT 的信息展示,就是通過上述方法去鏈上抓取數據,並在 OpenSea 自己的系統中來建立起來所有資源信息,最後通過 Web 的形式展現到大家面前,方便大家瀏覽。

這裏需要吐槽的是 OpenSea 網站的 robust 及 performance 做得實在太爛了,經常動不動就掛掉,所以鄙人也正著手搞一個類似的產品,從 blockchain 上同步 NFT 數據,以及從 OpenSea 上同步交易數據,存儲後按用戶需求展示給用戶,提供穩定、高效以及易用的瀏覽及查詢服務。(做得好的話未來想象空間也很大,哈哈)

上面通過腳本調用合約給大家展示的都是「」方法,「」方法的話就是在代碼中加載錢包,簽名之後就可以了。(所以挺簡單的吧,大家也別被所謂「科學家」這個名字唬住了。簡單學習一下編程,再熟悉下合約的調用,就可以通過程序操作多個錢包,去薅羊毛還是幹點別的事情就都可以了

需要警惕的一些方法

ERC721 規範中有兩個方法需要大家警惕一下,如果不小心也許就會丟掉你的 NFT。

第一個需要警惕的方法是「approve(to, tokenId)」,這個方法是授權「to」這個地址有權利可以轉走你這個「tokenId」的 NFT。如果你在小狐貍中授權釣魚網站調用了這個方法,最多會損失一個 NFT。

這個方法 ERC20 規範中也有,ERC20 是授權「to」地址最多可以使用多少數量的 token。鄙人的去中心化紅包項目,就是在你發紅包之前要求你使用「approve」授權一下,好讓紅包合約可以扣你的 token 並裝入紅包中給大家去搶。各類 Swap 的 DAPP 也是一樣,需要先「approve」才可以進行 swap。

第二個需要警惕的方法是「setApprovalForAll(operator, approved)」,這個方法是授權「operator」這個地址可以轉走你在這個 Collection 下所有的 NFT。如果你在小狐貍中授權釣魚網站調用了這個方法,則可能丟失這個 Collection 下的所有 NFT。

在 OpenSea 平臺中,如果我們「Sell」一個 NFT,小狐貍就會彈出這個方法的授權,見下面截圖。

註意紅框裏的內容,就是向 CryptoApes 的合約地址調用「setApprovalForAll(operator, approved)」這個方法。授權之後,如果有人出價購買,OpenSea 的交易合約則可以把你的 NFT 直接轉給買家,轉給買家這個操作調用的是 ERC721 規範中的方法「transferFrom(from, to, tokenId)」,from 是你自己,to 是買家,tokenId 是這個 NFT 的編號。

所以大家在小狐貍中進行授權的時候,一定有安全意識,看見 approve 或者 setApprovalForAll 方法時一定要註意是不是正規的網站,合約地址是不是正確的地址。如果不小心授權錯了,你的 NFT 就可能被轉走。

也許有人會問:我只賣出一個 NFT,為什麽不單獨授權這一個出售的 NFT(approve),而是要授權所有的 NFT 呢(setApproveForAll)?

答:OpenSea 的解釋是可以省 gas 費,一次授權後,再次賣出其余 NFT 的時就不需要因為再次授權而付更多的 gas 費用了。

也許有人會問:那我授權給了 OpenSea 所有 NFT 的轉移權限,那 OpenSea 平臺會不會悄悄轉走我的 NFT 呢?

關於這個這個問題可以繼續看後面。

OpenSea 買賣流程的背後

賣出 NFT 的背後

下圖是在你賣出 NFT 時候的彈窗截圖:

在 OpenSea 上進行賣出操作時,會彈出窗口第一步讓你先初始化錢包(這個是一次性操作),在你付了 gas 費之後,OpenSea 的 Registry 合約會幫你創建一個錢包合約(實際上就是一個 Proxy 合約),大家如果在 etherscan 上查的話,可以看到一個 RegisterProxy 的操作,實際上調用的就是下面代碼去創建了一個屬於你個人錢包合約:

function registerProxy()
public
returns (OwnableDelegateProxy proxy)
{
require(proxies[msg.sender] == address(0));
// 創建一個新的代理合約
proxy = new OwnableDelegateProxy(msg.sender, delegateProxyImplementation, abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this)));
proxies[msg.sender] = proxy;
return proxy;
}

為什麽需要創建這個合約呢?主要目的是為了安全,因為第二步「Approve this item for sale」時需要授權一個合約地址可以轉移你的 NFT(也就是上一段落說的 setApprovalForAll 方法),授權可以轉移你 NFT 的地址,就是「Initialize your wallet」這一步所創建的錢包合約地址。

也就是說 OpenSea 不能直接轉走你的 NFT,只有你在 OpenSea 上初次創建的這個錢包地址才可以轉走。

在創建完錢包以及授權 NFT 之後,如果你去掛出同個 Collection 下的 NFT 進行賣出時,不需要額外的手續費,OpenSea 僅僅驗證你的簽名就可以掛出賣單了,這點就是被 OpenSea 宣傳的「gas-free listing」。

所以講到這裏,也許有人又註意到了一個問題:為什麽後續掛出賣單僅僅只需要簽名,不需要 transaction 呢?OpenSea 的賣單信息只是存到它自己的中心化服務器上,沒有存在鏈上嗎?

答:是的,賣單信息只存在 OpenSea 的中心化服務器上,沒有上鏈,具體可以參考這裏 OpenSea 的解釋。

在 OpenSea 上,大多數操作都是 脫鏈的,這意味著它們生成的訂單存儲在我們的系統中,並且可以通過來自另一個用戶的匹配訂單來完成。

當用戶列出要出售的物品時,他們只需簽署交換物品以進行付款的意圖。此意圖作為 賣單存儲在 OpenSea 系統中,並且不會創建交易。

關於很多評論說 OpenSea 太過中心化這個問題,之後鄙人也會簡單談談自己觀點。

買入 NFT 的背後

在 OpenSea 買入的時候,買賣的撮合其實是發生在 OpenSea 的中心化系統中,匹配好訂單後讓用戶調用 OpenSea 交易合約地址AtomicMatch 方法完成交易,這個方法裏完成了一系列復雜操作,這裏就不展開討論了。

也就是說成交之後,成交訂單的信息會上鏈,畢竟這涉及到了 Token 和 NFT 的轉移。

OpenSea 使用的交易合約應該是 Wyvern 協議,實際上如果整個交易過程中只有最終交易數據才上鏈的話,可以不用這麽復雜的合約,不過這應該是歷史遺留包袱。

大家只需要知道這次買入交易成功的背後,會完成這兩個步驟:

  1. 把 NFT 轉給買家
  2. 把買家的錢轉給賣家和 OpenSea(平臺手續費)
其它交易方式

關於 Offer 報價以及 Auction 拍賣的交易方式鄙人沒有試過,不過我認為跟 Listing 交易一樣,在 offer 或者 bid 之前 approve 一下你的 WETH 就行,而 auction 拍賣訂單和 Offer 報價訂單的創建與撮合也應該還是通過 OpenSea 的中心化系統完成

關於 OpenSea 太過中心化的評論

鄙人在網絡上看到很多聲討 OpenSea 太過中心化的評論,這段時間開始買 NFT 對 OpenSea 進行研究了之後才了解這些評論的根本原因,實際上 OpenSea 也是基於當時條件限製下的產物。

鄙人這裏也為 OpenSea 的部分中心化技術方案辯駁幾句:

  • 首先它降低了手續費,貴族鏈絕非浪得虛名的。如果所有數據信息上鏈那必然會導致交易的成本上升,更加提高了用戶的交易門檻。(如果所有信息上鏈的話,我想唯一的好處就是鄙人不會每天收到批量 offer 的郵件通知了,畢竟每一次 offer 都需要燃燒 gas)
  • 其次 Etheruem 的 performance 不足以承載大量的 transaction,如果每次掛出賣單、修改賣單價格、每次 Offer 價格等所有信息都上鏈的話,那會更加進一步推高 Ethereum 的 gas 費用。
  • 最後關鍵信息上鏈也一定程度上確保了交易的安全和公開。比如你 NFT 和 WETH 的授權是給到你的錢包合約的,OpenSea 不直接觸碰。OpenSea 也開放了它的 API,所有未上鏈的訂單數據可以通過 API 獲取,好心人可以通過 API 拿到數據後和鏈上最終的交易數據進行比對驗證。(不過我猜測不存在這樣的好心人吧)

最近這一年公鏈的迅猛發展,gas 費過高以及性能的問題以及得到了極大的緩解。如果 OpenSea 不思進取、不做改進的話絕對會被大多數用戶拋棄,最後發展成為貴族 NFT 市場,比如前幾天 LooksRare 平臺也來空投搶用戶了。當然這個問題也是 Ethereum 需要面對的。

關於 OpenSea 的交易背後本人有兩個問題:

  1. 本人發現在 OpenSea 上 Cancel 一個 Listing 訂單,也是需要寫入區塊鏈的。但我覺得直接在 OpenSea 的中心化系統中直接 Cancel 就好了,畢竟 Listing 的訂單信息也沒上鏈啊,為什麽 Cancel Listing 訂單的操作需要上鏈呢?這 gas 費不是白白浪費掉了嗎?
  2. Listing 訂單價格的修改,只能往低了修改,不能改高了,如果需要報更高價格的話需要先 Cancel Listing 訂單,然後再重新掛一個,而 Cancel 的時候又得上鏈浪費一筆 gas 費用。同樣 Listing 訂單數據沒在鏈上,為什麽 List price 都可以往低了改,而不能直接改高呢?

這兩個問題本人沒太想明白為什麽 OpenSea 這樣做,因為個人覺得在中心化系統上取消訂單和隨意修改訂單價格不是很簡單的事情嗎?

個人不懷好意的猜測是 OpenSea 系統做得太爛,不願意讓用戶隨意取消訂單和修改價格,因為這樣會導致系統的不穩定。所以 Cancel Listing 這個操作硬要上鏈,讓用戶消耗 gas,這樣用戶在掛賣單的時候就需要慎重考慮要不要掛,以及要掛的價格了。

也希望知道原因的朋友不吝賜教!

最後的最後

最近因為 PhantaBear 的關系對 NFT 著迷了,鄙人著迷到什麽程度呢?我媳婦告訴我,我小學 2 年級的孩子都在問她同學有沒有爆炸頭熊的 NFT 了。。。

著迷到已經有點動搖 BTC Hodl 決心,盤算著是不是拿點 BTC 去換一個 CryptoPunks 的程度,畢竟有 NFT 第一的故事加持,非常想搞一枚。

關於 NFT 的胡思亂想

社會的經濟活動實際上就是錢和資產相互兌換的過程。比如用錢購買衣服、鞋、車、房就是用錢購買了資產。同樣也可以變賣衣服、鞋、車、房將資產換取錢,這就是基本的經濟活動。這個交易過程的順利執行其實是通過合同保證的(買衣服、鞋這類小商品雖然沒有實體合同,但實際上存在虛擬合同),而社會法律強製確保了合同可以按預期執行(你要是收了錢但沒給我東西,我就可以起訴你)。

你會看到在區塊鏈的世界中是一模一樣的,BTC、ETH 等各種幣就是錢,而玲瑯滿目的 NFT 就是個各類資產。crypto 可以購買 NFT,NFT 可以變賣換成 crypto,這樣就形成了未來元宇宙中的經濟活動。交易通過智能合約來完成,而公鏈的共識機製又確保了智能合約能夠以預期方式執行。

你會發現區塊鏈行業的發展與現實社會極其相似,NFT 的出現也許不是偶然吧。

不過比較有意思的是,人類社會最早的經濟活動只是以物換物,例如我用羊去換你的牛,因為以物換物的不便才出現了貨幣作為交換的媒介。所以現實社會是先出現資產,再出現貨幣。但區塊鏈行業中先出現的是各類加密貨幣,然後才出現了代表資產的 NFT,與現實社會相反。

投資建議聲明

以上文章內容全是鄙人胡說八道,不作為任何投資建議。

免責聲明:作為區塊鏈信息平臺,本站所發布文章僅代表作者及嘉賓個人觀點,與 Web3Caff 立場無關。文章內的信息均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。