Skip to content

Instantly share code, notes, and snippets.

@EmmanuelAdeiza
Created June 8, 2022 05:37
Show Gist options
  • Save EmmanuelAdeiza/398f417d981acb936109d9867865c394 to your computer and use it in GitHub Desktop.
Save EmmanuelAdeiza/398f417d981acb936109d9867865c394 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.14+commit.80d49f37.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: MIT
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";
// 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";
// Dev imports. This only works on a local dev network
// and will not work on any test or main livenets.
import "hardhat/console.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
contract BullBear is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable, KeeperCompatibleInterface, VRFConsumerBaseV2 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
uint public interval;
uint public lastTimeStamp;
int256 public currentPrice;
enum MarketTrend{BULL, BEAR}
MarketTrend public currentMarketTrend = MarketTrend.BULL;
VRFCoordinatorV2Interface COORDINATOR;
// Your subscription ID.
uint64 s_subscriptionId = 6066;
// Rinkeby 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;
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 = 1;
uint256[] public s_randomWords;
uint256 public s_requestId;
AggregatorV3Interface public priceFeed ;
// IPFS URIs for the dynamic nft graphics/metadata.
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"
];
event TokensUpdated(string marketTrend);
constructor(uint updateInterval, address _priceFeed, uint64 subscriptionId) ERC721("BullBear", "MTK") VRFConsumerBaseV2(vrfCoordinator) {
// Sets the keeper update interval
interval= updateInterval;
lastTimeStamp = block.timestamp;
// Sets the price feed address to
// BTC/USD Price Feed Contract address on Rinkeby: https://rinkeby.etherscan.io/address/0xECe365B379E1dD183B20fc5f022230C044d51404
// or the MockPriceFeed contract
priceFeed= AggregatorV3Interface(_priceFeed);
currentPrice = getLatestPrice();
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
s_subscriptionId = subscriptionId;
}
function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
// Defaults to gamer bull NFT Image
string memory defaultUri = bullUrisIpfs[0];
_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){
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){
console.log("NO CHANGE IN PRICE -> returning!");
return;
}
if(latestPrice < currentPrice){
// bear
currentMarketTrend = MarketTrend.BEAR;
// updateAllTokenUris("bears");
} else{
// bull
currentMarketTrend = MarketTrend.BULL;
// updateAllTokenUris("bull");
}
// Initiate the VRF calls to get a random number (word)
// that will then be used to to choose one of the URIs
// that gets applied to all minted tokens.
requestRandomnessForNFTUris();
// update currentPrice
currentPrice = latestPrice;
} else {
console.log(
" INTERVAL NOT UP!"
);
return;
}
}
/**
* Returns the latest price
*/
function getLatestPrice() public view returns (int256) {
(
/*uint80 roundID*/,
int price,
/*uint startedAt*/,
/*uint timeStamp*/,
/*uint80 answeredInRound*/
) = priceFeed.latestRoundData();
// An example of what the return value from a price feed looks like is: int256 3034715771688
return price;
}
// Assumes the subscription is funded sufficiently.
function requestRandomnessForNFTUris() internal {
require(s_subscriptionId != 0, "Subscription ID not set");
// Will revert if subscription is not set and funded.
s_requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
console.log("Request ID: ", s_requestId);
// requestId looks like uint256: 80023009725525451140349768621743705773526822376835636211719588211198618496446
}
function fulfillRandomWords(
uint256, /* requestId */
uint256[] memory randomWords
) internal override {
s_randomWords = randomWords;
console.log("...Fulfilling random Words");
string[] memory urisForTrend = currentMarketTrend == MarketTrend.BULL ? bullUrisIpfs : bearUrisIpfs;
uint256 idx = randomWords[0] % urisForTrend.length; // use modulo to choose a random index.
for (uint i = 0; i < _tokenIdCounter.current() ; i++) {
_setTokenURI(i, urisForTrend[idx]);
}
string memory trend = currentMarketTrend == MarketTrend.BULL ? "bullish" : "bearish";
emit TokensUpdated(trend);
}
function updateAllTokenUris(string memory trend) internal {
if (compareString("bear", trend)){
for (uint i=0;i < _tokenIdCounter.current(); i++){
_setTokenURI(i, bearUrisIpfs[0]);
}
} else {
for (uint i=0;i < _tokenIdCounter.current(); i++){
_setTokenURI(i, bullUrisIpfs[0]);
}
}
emit TokensUpdated(trend);
}
function setInterval (uint256 newInterval) public onlyOwner {
interval = newInterval;
}
// For VRF Subscription Manager
function setSubscriptionId(uint64 _id) public onlyOwner {
s_subscriptionId = _id;
}
function setSubscriptionId(uint32 maxGas) public onlyOwner {
callbackGasLimit = maxGas;
}
function setVrfCoodinator(address _address) public onlyOwner {
COORDINATOR = VRFCoordinatorV2Interface(_address);
}
function setPriceFeed (address newFeed) public onlyOwner {
priceFeed = AggregatorV3Interface(newFeed);
}
// Helpers
function compareString(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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment