短评第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介绍文档:

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

3.1.1 GetCollections 获取收藏

请求地址:

返回数据结构体:

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获取收藏

请求地址:

返回数据结构体:

{
    "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 获取资产事件

请求地址:

返回数据结构体:

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

3.1.5 GetAsset 获取资产

请求地址:

返回数据结构体:

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 获取每项资产的所有价格

请求地址:

返回数据结构体:

  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 获取资产的当前价格

请求地址:

返回数据结构体:

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 获取用户

请求地址 :

返回数据结构体:

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()}
Subscribe to ViewDAO
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.