Author:ViewDAO.DaPangDun & ViewDAO.askgo
自从《关于NFT流动性的研究》和《NFT预言机赛道分析》之后,至今已有一月有余,NFTFi的相关项目也在不断发展中,其中BendDAO也引发了不少的关注,在这个过程当中,项目对于预言机的需求也变得越来越多。
我个人目前也在深度参与一个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)成为以上相关项目的其中一个数据源来提供价格数据。
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的预测会因为元数据的问题而具有更高的不确定性。
之前提到过两个我们看好的NFT预言机项目,即:upshot和banksea。我们的一个重要的理念是持续的追踪项目的情况,中间更新过这两个项目的一些信息,这次借本文更新一下对二者API的测试情况:
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是开放的,我们测试了没有调用频率的限制(未来应该会加上限制)
目前只提供了一个接口 用来取价格 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()}