Cover photo

短评第7期:再谈NFT预言机

Author:ViewDAO.DaPangDun & ViewDAO.askgo

1、NFTFi的发展

自从《关于NFT流动性的研究》和《NFT预言机赛道分析》之后,至今已有一月有余,NFTFi的相关项目也在不断发展中,其中BendDAO也引发了不少的关注,在这个过程当中,项目对于预言机的需求也变得越来越多。

我个人目前也在深度参与一个NFTFi的相关项目,未来如果有起色会公布出来

NFTFi项目图谱(部分)
NFTFi项目图谱(部分)

概括来说,NFT预言机主要可以用于以下几个方面:

1)NFT借贷:为借贷项目提供NFT价格,通常为floor price,用于NFT的清算环节;

2)NFT衍生品:如NFT合约,为NFT提供市场价格,可能为floor price,也可能是actual price,一般用于利润或亏损计算、衍生品清算环节;

3)NFT数据平台:提供NFT的实时价格、系列floor价格等数据,为数据分析和展示做支撑;

4)NFT租赁或其他NFTFi项目:提供NFT价格数据的参考,让用户在评估NFT价值时拥有一个大致的参考标准;

5)成为以上相关项目的其中一个数据源来提供价格数据。

2、NFT预言机的几种机制

NFT的定价机制有【手动定价】和【自动定价】两个大的方向,对于预言机而言,必然是走自动定价这个方向,这也是未来发展必然的趋势。

常见的NFT预言机原理有三种:

2.1 博弈定价

即Abacus Spot类型的定价方式,通过建立池子然后进行买卖博弈的形式来提供实时的实际的价格;

2.2 加权算法定价

即Chainlink类型的定价形式,通过对于相关价格(主要是floor price)进行时间加权的形式然后来提供实时的、平滑的、floor价格;

2.3 机器学习算法定价

即类似于upshot或者banksea的方式,通过相关联的数据、特征值等来对每个NFT(通常必须要有一些交易数据)提供实时的、预测的、偏实际的价格。

从市场的角度来说:

1)floor price具有更加广泛的应用(比如当前的NFT借贷基本都是采用的floor price,一些NFT的衍生品也是采用的floor price);

2)一个大的争论点就是在于NFT的稀有度不同造成的NFT的内在价值是不同的,如果用floor price来衡量相对来说是不够公平的;

3)NFT实际价值的定价需求是客观存在的,这也促使了相关定价项目的发展,其中预测、时间加权、实时博弈是目前几个主要的方向,预测会有超前性和不准确性、时间加权会有滞后性、实时博弈会有失衡的可能性;

4)由于NFT的交易频率和元数据的个数不同,floor price的计算或预测相对来说会更有保证,单个NFT的预测会因为元数据的问题而具有更高的不确定性。

3、两个项目的API测试情况

之前提到过两个我们看好的NFT预言机项目,即:upshot和banksea。我们的一个重要的理念是持续的追踪项目的情况,中间更新过这两个项目的一些信息,这次借本文更新一下对二者API的测试情况:

3.1 Upshot

upshot的API介绍文档:

https://docs.upshot.xyz/upshot-api/

我们测试总计9个接口,分述如下:

3.1.1 GetCollections 获取收藏

请求地址:

https://api.upshot.io/v1/collections

返回数据结构体:

type Result struct {
    Status bool `json:"status"`  
    Message string `json:"message"` 
    Data struct {
        Count int `json:"count"`
        Collections []struct {
            ID int `json:"id"`  
            Name string `json:"name"`
            Description string `json:"description"`
            ImageURL string `json:"imageUrl"`
            Slug string `json:"slug"`
        } `json:"collections"`
    } `json:"data"`
}

3.1.2 GetCollectionsBySlug 通过slug获取收藏

请求地址:

https://api.upshot.io/v1/collections/slug/coquina-by-jacob-gold

返回数据结构体:

{
    "status": true,
    "message": "collection retrieved successfully",
    "data": {
        "id": 1520,
        "name": "Coquina",
        "description": "Organic....",
        "imageUrl": "https://api.artblocks.io/image/198000000",
        "slug": "coquina-by-jacob-gold"
    }
}

3.1.3 GetCollectionByContractAddress 通过合约地址获取收藏

请求地址:https://api.upshot.io/v1/collections/contractAddress/0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB

返回数据结构体:

{
    "status": true,
    "message": "collection retrieved successfully",
    "data": {
        "id": 1,
        "name": "CryptoPunks",
        "description": "CryptoPunks....",
        "imageUrl": "https://lh3.googleusercontent.com/BdxvLseXcfl57BiuQcQYdJ64v-aI8din7WPk0Pgo3qQFhAUH-B6i-dCqqc_mCkRIzULmwzwecnohLhrcH8A9mpWIZqA7ygc52Sr81hE=s120",
        "slug": "cryptopunks"
    }
}

3.1.4 GetAssetEvents 获取资产事件

请求地址:

https://api.upshot.io/v1/assets/events?assetId=0xe4605d46Fd0B3f8329d936a8b258D69276cBa264/439&type=SALE&marketType=SECONDARY

返回数据结构体:

{
    "status": true,
    "message": "Asset Events gotten successfully",
    "data": []
}

3.1.5 GetAsset 获取资产

请求地址:

https://api.upshot.io/v1/assets?assetId=0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB/0

返回数据结构体:

type Result struct {
    Status bool `json:"status"`
    Message string `json:"message"`
    Data struct {
        Count int `json:"count"`
        Assets []struct {
            AssetID string `json:"assetId"`
            TokenID string `json:"tokenId"`
            Name string `json:"name"`
            Description string `json:"description"`
            CreatorAddress string `json:"creatorAddress"`
            MediaURL string `json:"mediaUrl"`
            TokenURI interface{} `json:"tokenUri"`
            ContractAddress string `json:"contractAddress"`
            PreviewImageURL string `json:"previewImageUrl"`
            MediaType string `json:"mediaType"`
            SourceType string `json:"sourceType"`
            TxBlockNumber string `json:"txBlockNumber"`
            TxHash string `json:"txHash"`
            TxAt int `json:"txAt"`
            Contract struct {
                Address string `json:"address"`
                Name string `json:"name"`
                ImageURL string `json:"imageUrl"`
                Description string `json:"description"`
                TotalSupply interface{} `json:"totalSupply"`
                SchemaType string `json:"schemaType"`
                Symbol interface{} `json:"symbol"`
                ChainID int `json:"chainId"`
            } `json:"contract"`
            Traits []struct {
                TraitID int `json:"traitId"`
                Trait struct {
                    TraitType string `json:"traitType"`
                    DisplayType string `json:"displayType"`
                    Value string `json:"value"`
                } `json:"trait"`
            } `json:"traits"`
        } `json:"assets"`
    } `json:"data"`
}

3.1.6 GetAllPricesPerAsset 获取每项资产的所有价格

请求地址:

https://api.upshot.io/v1/prices?assetId=0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB/11

返回数据结构体:

  type Result  struct {
    Status bool `json:"status"`
    Message string `json:"message"`
    Data struct {
        Count int `json:"count"`
        Pricings []struct {
            AssetID string `json:"assetId"`
            EstimatedPrice string `json:"estimatedPrice"`
            Low string `json:"low"`
            High string `json:"high"`
            Confidence float64 `json:"confidence"`
            Source string `json:"source"`
            Timestamp int `json:"timestamp"`
            ResolutionID interface{} `json:"resolutionId"`
            CollectionID int `json:"collectionId"`
            Agreement interface{} `json:"agreement"`
            CertificationTimestamp interface{} `json:"certificationTimestamp"`
            EthSalePrice string `json:"ethSalePrice"`
            UsdSalePrice string `json:"usdSalePrice"`
            MedianRelativeError float64 `json:"medianRelativeError"`
            Currency struct {
                ID int `json:"id"`
                Symbol string `json:"symbol"`
                Name string `json:"name"`
                Decimals int `json:"decimals"`
                ContractAddress string `json:"contractAddress"`
                Description interface{} `json:"description"`
                ImageURL string `json:"imageUrl"`
                CoinGeckoID string `json:"coinGeckoId"`
                ChainID int `json:"chainId"`
            } `json:"currency"`
            Asset struct {
                ID string `json:"id"`
                TokenID string `json:"tokenId"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatorAddress string `json:"creatorAddress"`
                MediaURL string `json:"mediaUrl"`
                TokenURI interface{} `json:"tokenUri"`
                ContractAddress string `json:"contractAddress"`
                MediaType string `json:"mediaType"`
                SourceType string `json:"sourceType"`
                TxBlockNumber string `json:"txBlockNumber"`
                TxHash string `json:"txHash"`
                TxAt int `json:"txAt"`
                PreviewVideoURL interface{} `json:"previewVideoUrl"`
                Rarity float64 `json:"rarity"`
                RarityProcessed bool `json:"rarityProcessed"`
                Allowed bool `json:"allowed"`
                Popular bool `json:"popular"`
                Priority string `json:"priority"`
                LastSaleAt interface{} `json:"lastSaleAt"`
                LastSaleWeiPrice interface{} `json:"lastSaleWeiPrice"`
                LastAppraisalAt int `json:"lastAppraisalAt"`
                LastAppraisalWeiPrice string `json:"lastAppraisalWeiPrice"`
                TotalOwners int `json:"totalOwners"`
                LastSaleUsdPrice interface{} `json:"lastSaleUsdPrice"`
                LastAppraisalUsdPrice string `json:"lastAppraisalUsdPrice"`
                PreviewImageWidth int `json:"previewImageWidth"`
                PreviewImageHeight int `json:"previewImageHeight"`
                WarningBanner bool `json:"warningBanner"`
                CollectionID int `json:"collectionId"`
                CreatedAt time.Time `json:"createdAt"`
                UpdatedAt time.Time `json:"updatedAt"`
                DeletedAt interface{} `json:"deletedAt"`
            } `json:"asset"`
        } `json:"pricings"`
    } `json:"data"`
}

3.1.7 GetCurrentPricesForAnAsset 获取资产的当前价格

请求地址:

https://api.upshot.io/v1/prices/latest?assetId=0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB/11

返回数据结构体:

type Result struct {
    Status bool `json:"status"`
    Message string `json:"message"`
    Data []struct {
        AssetID string `json:"assetId"`
        CurrentPricing struct {
            AssetID string `json:"assetId"`
            EstimatedPrice string `json:"estimatedPrice"`
            Low string `json:"low"`
            High string `json:"high"`
            Confidence float64 `json:"confidence"`
            Source string `json:"source"`
            Timestamp int `json:"timestamp"`
            ResolutionID interface{} `json:"resolutionId"`
            CollectionID int `json:"collectionId"`
            Agreement interface{} `json:"agreement"`
            CertificationTimestamp interface{} `json:"certificationTimestamp"`
            EthSalePrice string `json:"ethSalePrice"`
            UsdSalePrice string `json:"usdSalePrice"`
            MedianRelativeError float64 `json:"medianRelativeError"`
            Currency struct {
                ID int `json:"id"`
                Symbol string `json:"symbol"`
                Name string `json:"name"`
                Decimals int `json:"decimals"`
                ContractAddress string `json:"contractAddress"`
                Description interface{} `json:"description"`
                ImageURL string `json:"imageUrl"`
                CoinGeckoID string `json:"coinGeckoId"`
                ChainID int `json:"chainId"`
            } `json:"currency"`
            Asset struct {
                ID string `json:"id"`
                TokenID string `json:"tokenId"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatorAddress string `json:"creatorAddress"`
                MediaURL string `json:"mediaUrl"`
                TokenURI interface{} `json:"tokenUri"`
                ContractAddress string `json:"contractAddress"`
                MediaType string `json:"mediaType"`
                SourceType string `json:"sourceType"`
                TxBlockNumber string `json:"txBlockNumber"`
                TxHash string `json:"txHash"`
                TxAt int `json:"txAt"`
                PreviewVideoURL interface{} `json:"previewVideoUrl"`
                Rarity float64 `json:"rarity"`
                RarityProcessed bool `json:"rarityProcessed"`
                Allowed bool `json:"allowed"`
                Popular bool `json:"popular"`
                Priority string `json:"priority"`
                LastSaleAt interface{} `json:"lastSaleAt"`
                LastSaleWeiPrice interface{} `json:"lastSaleWeiPrice"`
                LastAppraisalAt int `json:"lastAppraisalAt"`
                LastAppraisalWeiPrice string `json:"lastAppraisalWeiPrice"`
                TotalOwners int `json:"totalOwners"`
                LastSaleUsdPrice interface{} `json:"lastSaleUsdPrice"`
                LastAppraisalUsdPrice string `json:"lastAppraisalUsdPrice"`
                PreviewImageWidth int `json:"previewImageWidth"`
                PreviewImageHeight int `json:"previewImageHeight"`
                WarningBanner bool `json:"warningBanner"`
                CollectionID int `json:"collectionId"`
                CreatedAt time.Time `json:"createdAt"`
                UpdatedAt time.Time `json:"updatedAt"`
                DeletedAt interface{} `json:"deletedAt"`
            } `json:"asset"`
        } `json:"currentPricing"`
    } `json:"data"`
}

3.1.8 GetUser 获取用户

请求地址 :

https://api.upshot.io/v1/users/0xc896866e927e6f8a416ba209976115e79fa0a66f

返回数据结构体:

type Result struct {
    Status bool `json:"status"`
    Message string `json:"message"`
    Data struct {
        UserAddress string `json:"userAddress"`
        Ens interface{} `json:"ens"`
        AppraisedWeiSum string `json:"appraisedWeiSum"`
        AppraisedUSDSum string `json:"appraisedUSDSum"`
        AssetOwned []struct {
            AssetID string `json:"assetId"`
            TotalEditions string `json:"totalEditions"`
            AcquiredTimestamp int `json:"acquiredTimestamp"`
            SentTimestamp interface{} `json:"sentTimestamp"`
            Asset struct {
                ID string `json:"id"`
                TokenID string `json:"tokenId"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatorAddress string `json:"creatorAddress"`
                MediaURL string `json:"mediaUrl"`
                TokenURI string `json:"tokenUri"`
                ContractAddress string `json:"contractAddress"`
                PreviewImageURL string `json:"previewImageUrl"`
                MediaType string `json:"mediaType"`
                SourceType string `json:"sourceType"`
                TxBlockNumber string `json:"txBlockNumber"`
                TxHash string `json:"txHash"`
                TxAt int `json:"txAt"`
                LastSaleAt interface{} `json:"lastSaleAt"`
                LastSaleWeiPrice interface{} `json:"lastSaleWeiPrice"`
                LastAppraisalAt int `json:"lastAppraisalAt"`
                LastAppraisalWeiPrice string `json:"lastAppraisalWeiPrice"`
                TotalOwners int `json:"totalOwners"`
                LastSaleUsdPrice interface{} `json:"lastSaleUsdPrice"`
                LastAppraisalUsdPrice string `json:"lastAppraisalUsdPrice"`
            } `json:"asset"`
        } `json:"assetOwned"`
    } `json:"data"`
}

3.1.9 GetMultipleUsers 获取多个用户

请求地址:

https://api.upshot.io/v1/users?addressOrENS=["0xB4460Bdf3Fe5Ba4e25f96135A76BDCb9E45AB010", "don-luv.eth"]

返回数据结构体:

type Result struct {
    Status bool `json:"status"`
    Message string `json:"message"`
    Data []struct {
        UserAddress string `json:"userAddress"`
        Ens interface{} `json:"ens"`
        AppraisedWeiSum string `json:"appraisedWeiSum"`
        AppraisedUSDSum string `json:"appraisedUSDSum"`
        AssetOwned []struct {
            AssetID string `json:"assetId"`
            TotalEditions string `json:"totalEditions"`
            AcquiredTimestamp int `json:"acquiredTimestamp"`
            SentTimestamp interface{} `json:"sentTimestamp"`
            Asset struct {
                ID string `json:"id"`
                TokenID string `json:"tokenId"`
                Name string `json:"name"`
                Description string `json:"description"`
                CreatorAddress string `json:"creatorAddress"`
                MediaURL string `json:"mediaUrl"`
                TokenURI string `json:"tokenUri"`
                ContractAddress string `json:"contractAddress"`
                PreviewImageURL string `json:"previewImageUrl"`
                MediaType string `json:"mediaType"`
                SourceType string `json:"sourceType"`
                TxBlockNumber string `json:"txBlockNumber"`
                TxHash string `json:"txHash"`
                TxAt int `json:"txAt"`
                LastSaleAt interface{} `json:"lastSaleAt"`
                LastSaleWeiPrice interface{} `json:"lastSaleWeiPrice"`
                LastAppraisalAt interface{} `json:"lastAppraisalAt"`
                LastAppraisalWeiPrice interface{} `json:"lastAppraisalWeiPrice"`
                TotalOwners int `json:"totalOwners"`
                LastSaleUsdPrice interface{} `json:"lastSaleUsdPrice"`
                LastAppraisalUsdPrice interface{} `json:"lastAppraisalUsdPrice"`
            } `json:"asset"`
        } `json:"assetOwned"`
    } `json:"data"`
}

目前API是开放的,我们测试了没有调用频率的限制(未来应该会加上限制)

3.2 Banksea

目前只提供了一个接口 用来取价格 Fetch price

3.2.1 测试 Ethereum 上数据

测试 Ethereum 上面的数据,测试NFT集为 BoredApeYachtClub BAYC #3421

Eethereum提供合约ID和TokenID

const report1 = await fetchTokenReport(program, {
  contractAddress: 0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
  tokenId: 3421
})

请求之后 成功返回:

{
  assetAddr: 'CzhLY2c312v8tRCTc4rfcaU6GYN9tVKQL6wZBdZqpyc1',
  decimal: '6',
  price: '19457051',
  priceType: 'ETH',
  risk: '9442',
  time: '1651201855',
  name: 'BAYC #3421'
}

3.2.2 测试Solana链上数据

测试 Solana 上面的数据,测试NFT集为 Degen Ape #110 项目市场位于 https://solanart.io/

Solana 上面 只需提供NFT的合约地址

const report2 = await fetchTokenReport(program, '7xZxwzmYVTfzvHR21fwp81PNy8dzMvN2DAmEi12WfRqA')

请求之后 成功返回:

{
  assetAddr: 'YZj65Lhtni37ZM8tqtxEUZLpD8Uzu1rouJcseLCtA6w',
  decimal: '6',
  price: '34656747',
  priceType: 'SOL',
  risk: '7604',
  time: '1651209074',
  name: 'Degen Ape #110'
}

测试 Solana 上面的数据,测试NFT集为 SolPunk_#3596 (SolPunks)项目市场位于 https://solanart.io/

报错信息为:

Account does not exist ${address.toString()}