作者:xing824.eth

來源: NFT及OpenSea交易背後的技術分享

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

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

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

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

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

總的來說就是在用戶心智中能夠佔據第一的項目,都是比較好的標的。比特幣是整個區塊鏈行業的開創者,所以是最值得投資的標的;以太坊佔據了擁有智能合約功能的第二代區塊鏈中的第一名,所以也是值得投資的(雖然我也沒有持倉);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的合約裡寫入了兩個信息:

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的解釋。

On OpenSea, most actions are off-chain, meaning they generate orders that are stored in the our system and can be fulfilled by a matching order from another user.

When a user lists an item for sale, they simply sign their intent to swap the item for payment. This intent is stored in the OpenSea system as a sell order, and does not create a transaction.

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

買入NFT的背後

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

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

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

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

把NFT轉給買家

把買家的錢轉給賣家和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的交易背後本人有兩個問題:

本人發現在OpenSea上Cancel一個Listing訂單,也是需要寫入區塊鏈的。但我覺得直接在OpenSea的中心化系統中直接Cancel就好了,畢竟Listing的訂單信息也沒上鍊啊,為什麼Cancel Listing訂單的操作需要上鍊呢?這gas費不是白白浪費掉了嗎?

Listing訂單價格的修改,只能往低了修改,不能改高了,如果需要報更高價格的話需要先Cancel Listing訂單,然後再重新掛一個,而Cancel的時候又得上鍊浪費一筆gas費用。同樣Listing訂單數據沒在鏈上,為什麼List price都可以往低了改,而不能直接改高呢?

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

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

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

上面兩個問題的解答(2022-01-25更新)

昨天opensea出現了一個問題,很多人高價的NFT以特別低的價格被出售了,損失慘重。本人今天研究了下,順便這個問題同步更新一下。

在OpenSea官網中有這麼一篇文章教用戶如何取消訂單的,注意截圖裡的這段話:

大意是如果你掛出過你NFT的賣單,這時你又把你NFT轉到另外一個錢包地址,這種情況不會自動取消你NFT的賣單,所以你應該在轉走你NFT之前先把賣單取消掉。

而這次出現損失的用戶大部分就是這個原因了。例如幾個月前, 用戶A把他的NFT掛出10ETH的價格出售,當時價格未成交,這時用戶A未取消賣單就直接把NFT轉到另外的錢包地址(可能是為了安全轉到冷錢包)去了。

過了幾個月後發現市場價格不錯,就把NFT又轉回到之前的錢包地址,準備掛單出售,但當剛轉回成功的時候就發現NFT被以之前的賣單價格(10ETH)出售了,當前市場價可能是80ETH,造成了嚴重損失。

之所以可以被10ETH的賣單價格出售,就是因為之前的賣單信息被人盯上了,所以在轉回NFT的時候被人立馬以10ETH的低價購買成功。

合約分析

上面說過,opensea的賣單信息是中心化存儲的,這個賣單信息中包括了你NFT的信息、價格以及簽名,同時這個賣單信息可以通過opensea的API查詢到。

科學家可以通過opensea的API找到一些價值比較高的NFT的賣單信息,同時監控該NFT是否又被轉回到與該賣單信息一致的錢包地址,當發現轉回時,科學家調用前文所述的atomicMatch這個方法即可完成交易。

截圖中可以看到,atomicMatch方法裡有個requireValidOrder內部方法,該方法會去校驗這個賣單信息是否有效,簽名是否正確等。這個賣單信息是存在opensea上並通過API開放出來的,任何人拿到這個賣單信息並且提供能與之匹配的買單,就能完成交易。

上面這個截圖是購買時,requireValidOrder方法裡判斷賣單信息是否合法的實現,可以看到有一步是通過cancelledOrFinalized這個map類型變量來判斷的。

而之前說的取消訂單是一個上鍊操作,就是通過設置cancelledOrFinalized這個狀態來控制賣單信息失效的。

總而言之,就是一個用戶之前掛過低價賣單,後來NFT轉走了就再也沒管過這個賣單,而這個高價值NFT的低價賣單信息(包含簽名)又被人盯上了,所以當NFT又轉回來的時候就可以以之前的低價成交。

本人之所以有上一節的兩個問題,完全是因為沒看過opensea的API,沒想到opensea這麼open,把賣單的簽名信息也開放出來了,任何第三方拿到這個賣單信息都可以自己下單進行NFT的購買。

如何避免損失?

對於opensea來說,現在這個交易合約是n年前的產物,我覺得可以重新設計一個,做好遷移過度相關的工作,但這個工作量會比較高,費時比較長。我猜opensea已經在計劃了,因為opensea上polygon鏈的NFT已經不再是當前的交易方式。

另外對於opensea來說更簡單粗暴的方式就是賣單信息中的簽名別開放出去,為了安全更加中心化一些,防止不知情的用戶造成損失。

對於用戶來說,記得取消賣單,雖然費一些gas費吧。另外如果曾經掛過賣單且沒取消過的錢包,就別把NFT往迴轉了, 就用新錢包直接賣吧。

最後的最後

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

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

關於NFT的胡思亂想

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

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

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

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

投資建議聲明

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