Created
June 8, 2022 05:37
-
-
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=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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