2019年12月,Alchemy完成1500万美元A轮融资,资方为Pantera Capital,斯坦福大学,Coinbase,三星等。
2021年4月,Alchemy以5.05亿美元估值完成8000万美元B轮融资,Coatue和Addition领投,DFJ Growth、K5 Global、Chainsmokers(烟鬼组合)、演员Jared Leto和Glazer家族参投。
2021年10月,Alchemy以35亿美元估值完成2.5亿美元C轮融资,由a16z领投的。
2022年2月,Alchemy以102亿美元估值完成2亿美元融资,Lightspeed与Silver Lake领投。
Alchemy是一个背景强大、经费充足、踏实做事、没有发币的团队,这样的项目不刷,难道去刷土狗吗?最近帝哥已经陆陆续续把任务快做完了,现在在陆续给大家产出教程,帝哥也关注了下现在获取到的Alchemy的NFT的价格,大家可以去OpenSea上面查询下:
话不多说,来开始我们今天的教程,我是帝哥(@CoinmanLabs),今天帝哥带大家一起来看看第五周的任务。
一个动态 NFT是一种不可替代的代币,可以根据特定情况进行更改。
例如,目前有八种不同的LaMelo Ball NFT,每个 NFT 都会记录一组不同的 LaMelo 球员统计数据,从篮板和助攻到得分,并根据这些数据进行更改(10 次助攻?不同颜色 - 得分 1 分,不同球)。
动态 NFT 持有者可以根据 LaMelo 的持续表现获得抽奖和其他 NFT 特定福利的特殊访问权限。它变得更加凉爽;这八个 NFT 之一,Gold Evolve NFT,带来了一个独特的承诺:
如果拉梅洛·鲍尔(LaMelo Ball)赢得了 2021 年 NBA 赛季的年度最佳新秀,NFT 本身就会发展以反映新的形象。LaMelo 赢得了奖项,NFT 得到了发展。
在本教程中,将学会使用Chainlink 的去中心化和加密保护的预言机网络获取和跟踪资产价格数据,将学会使用来自Chainlink 守护者网络自动化NFT 智能合约,以根据正在跟踪的资产价格数据更新 NFT。如果市场价格上涨,智能合约将随机选择 NFT 的 URI 指向这三个看涨图像之一,并且 NFT 将动态更新。
Remix IDE和London VM
参考github地址:
IPFS,可以使用pinta,帝哥对此提到的工具
龙头和测试网代币:我们本次使用的网络是Rinkeby,一旦你的钱包连接到 Rinkeby,从 Alchemy 的 Rinkeby 水龙头获得 Rinkeby ETH.还需要获取测试网 LINK 代币.
对于本次课程的任务,将添加随机性,但将部署到以太坊的 Goerli 测试网。
如果需要 Goerli 测试网代币,从 Alchemy 的 Goerli 水龙头获取 Goerli ETH.
进入 https://faucets.chain.link/ 连接钱包,点击Send Request获取测试以太坊和link 代币。
如果你的钱包没有link这个代币显示,点击导入代币,输入合约地址(
0x01BE23585060835E02B77ef475b0Cc51aA1e0709)即可:
当你成功获取到测试币结束后,进入 https://vrf.chain.link/rinkeby 连接你的钱包,点击Create Subscription,来获取一个Link 预言机的订阅,订阅需要消耗少量gas费。
等待完成后别急着关页面,我们给他提供一些资金。点击add funds。
进入 https://remix.ethereum.org/ ,开始我们今天的项目开发,新建一个sol文件,结构和代码如下:
代码如下:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
// Chainlink Imports
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
// This import includes functions from both ./KeeperBase.sol and
// ./interfaces/KeeperCompatibleInterface.sol
import "@chainlink/contracts/src/v0.8/KeeperCompatible.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
// Dev imports. This only works on a local dev network
// and will not work on any test or main livenets.
import "hardhat/console.sol";
contract BullBear is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable, VRFConsumerBaseV2, KeeperCompatibleInterface {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
uint public interval;
uint public lastTimeStamp;
AggregatorV3Interface public priceFeed;
int256 public currentPrice;
// IPFS URIs for the dynamic nft graphics/metadata.
// NOTE: These connect to my IPFS Companion node.
// You should upload the contents of the /ipfs folder to your own node for development.
string[] bullUrisIpfs = [
"https://ipfs.io/ipfs/QmRXyfi3oNZCubDxiVFre3kLZ8XeGt6pQsnAQRZ7akhSNs?filename=gamer_bull.json",
"https://ipfs.io/ipfs/QmRJVFeMrtYS2CUVUM2cHJpBV5aX2xurpnsfZxLTTQbiD3?filename=party_bull.json",
"https://ipfs.io/ipfs/QmdcURmN1kEEtKgnbkVJJ8hrmsSWHpZvLkRgsKKoiWvW9g?filename=simple_bull.json"
];
string[] bearUrisIpfs = [
"https://ipfs.io/ipfs/Qmdx9Hx7FCDZGExyjLR6vYcnutUR8KhBZBnZfAPHiUommN?filename=beanie_bear.json",
"https://ipfs.io/ipfs/QmTVLyTSuiKGUEmb88BgXG3qNC8YgpHZiFbjHrXKH3QHEu?filename=coolio_bear.json",
"https://ipfs.io/ipfs/QmbKhBXVWmwrYsTPFYfroR2N7NAekAMxHUVg2CWks7i9qj?filename=simple_bear.json"
];
// random
VRFCoordinatorV2Interface COORDINATOR;
// Your subscription ID.
uint64 s_subscriptionId;
// Goerli coordinator. For other networks,
// see https://docs.chain.link/docs/vrf-contracts/#configurations
address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab;
// The gas lane to use, which specifies the maximum gas price to bump to.
// For a list of available gas lanes on each network,
// see https://docs.chain.link/docs/vrf-contracts/#configurations
bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc;
// Depends on the number of requested values that you want sent to the
// fulfillRandomWords() function. Storing each word costs about 20,000 gas,
// so 100,000 is a safe default for this example contract. Test and adjust
// this limit based on the network that you select, the size of the request,
// and the processing of the callback request in the fulfillRandomWords()
// function.
uint32 callbackGasLimit = 100000;
// The default is 3, but you can set this higher.
uint16 requestConfirmations = 3;
// For this example, retrieve 2 random values in one request.
// Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
uint32 numWords = 2;
uint256[] public s_randomWords;
uint256 public s_requestId;
event TokensUpdated(string marketTrend);
constructor(uint updateInterval, address _priceFeed, uint64 subscriptionId) ERC721("Bull&Bear", "BBTK") VRFConsumerBaseV2(vrfCoordinator) {
interval = updateInterval;
lastTimeStamp = block.timestamp;
// https://rinkeby.etherscan.io/address/0xECe365B379E1dD183B20fc5f022230C044d51404
priceFeed = AggregatorV3Interface(_priceFeed);
currentPrice = getLatestPrice();
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
s_subscriptionId = subscriptionId;
}
function safeMint(address to) public {
// Current counter value will be the minted token's token ID.
uint256 tokenId = _tokenIdCounter.current();
// Increment it so next time it's correct when we call .current()
_tokenIdCounter.increment();
// Mint the token
_safeMint(to, tokenId);
// Default to a bull NFT
string memory defaultUri = bullUrisIpfs[s_randomWords[0]%3];
_setTokenURI(tokenId, defaultUri);
console.log(
"DONE!!! minted token ",
tokenId,
" and assigned token url: ",
defaultUri
);
}
function checkUpkeep(bytes calldata) external view override returns (bool upkeepNeeded, bytes memory /*performData*/){
upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
}
function performUpkeep(bytes calldata) external override{
if((block.timestamp - lastTimeStamp) > interval){
lastTimeStamp = block.timestamp;
int latestPrice = getLatestPrice();
if(latestPrice == currentPrice){
return;
}else if(latestPrice < currentPrice){
updateAllTokenUris("bears");
}else{
updateAllTokenUris("bull");
}
currentPrice = latestPrice;
}
}
function getLatestPrice() public view returns(int256){
(,
int price,
,
,) = priceFeed.latestRoundData();
return price;
}
function updateAllTokenUris(string memory trend) internal{
if(compareStrings("bears", trend)){
for(uint i=0; i< _tokenIdCounter.current(); i++){
_setTokenURI(i,bearUrisIpfs[s_randomWords[0]%3]);
}
}else {
for(uint i=0; i< _tokenIdCounter.current(); i++){
_setTokenURI(i,bullUrisIpfs[s_randomWords[0]%3]);
}
}
emit TokensUpdated(trend);
}
function setInterval(uint256 newInterval) public onlyOwner{
interval = newInterval;
}
function setPriceFeed(address newFeed) public onlyOwner{
priceFeed = AggregatorV3Interface(newFeed);
}
function compareStrings(string memory a, string memory b) internal pure returns (bool){
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
// The following functions are overrides required by Solidity.
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}
function _burn(uint256 tokenId)
internal
override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
// Assumes the subscription is funded sufficiently.
function requestRandomWords() external onlyOwner {
// Will revert if subscription is not set and funded.
s_requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
}
function fulfillRandomWords(
uint256, /* requestId */
uint256[] memory randomWords
) internal override {
s_randomWords = randomWords;
}
}
id我们将需要部署的合约选择 Bull&Bear 合约,账号选择我们刚才领取测试币的账号,部署的参数UPDATEINTERVAL填写 10,_PRICEFEED 为 Link 上面测试ILV合约的预言机地址 0x48731cF7e84dc94C5f84577882c14Be11a5B7456
(合约地址来源https://docs.chain.link/docs/ethereum-addresses/),SUBSCRIPTIONID 为你自己申请的Link预言机订阅号。等待部署完成获取合约的地址。比如帝哥部署合约的地址:0xF0666b8ac53406aE3f630669Df6172BA0876C97b
将我们的合约地址添加到预言机的消费者中。如下面所示。
当添加成功后,我们在使用合约里面的函数来获取随机数。首先点击requestRandomWords函数,等待交易确认后在s_randomWords函数后输入0获取第一个数,我们可以稍微留意下现在这个数,过了一分钟,我们在重复现在做的事:首先点击requestRandomWords然后在点击s_randomWords函数后输入0。
我们在afemint里面填写自己的ETH地址,给自己mint一个NFT,等NFT mint成功以后,我们在tokenURI 这边输入0,NFT的元数据信息。
我们可以看到我们的元数据已经返回了。
我们在setPriceFeed方法里输入Link预言机以太坊价格的合约地址 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e,将把原来的LIV价格的预言机改ETH的,以点击一下 getLatestPrice方法,可以看到int256后面就是最新的以太坊价格,其中前4位是个位数,后面8位为小数,我们在performUpkeep方法的参数里输入“[]”,点击call,手动来触发价格更新方法,这一步方法里也会更新我们的NFT元数据我们再次点击获取Token Id 为0的NFT的元数据,可以看到元数据会变成这三个中的一个。
最后我们去 https://testnets.opensea.io/ 刷新元数据是否不一样了,下面的是帝哥做的哦。
以上就是第五周的任务了,大家别忘记了填写表格完成哦,表格页面在下方:
我是懂币帝,用技术带你领略区块链魅力,第一时间获取行业最新资讯:
推特:@CoinmanLabs
微信:CoinmanLabs(社群客服微信进群)