Skip to content

Commit 33180d9

Browse files
authored
Add Dynamic NFT example (#15)
* add dynamic NFT example
1 parent efefd0c commit 33180d9

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

dynamic-nft/1_starter.sol

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
5+
import "@openzeppelin/[email protected]/token/ERC721/extensions/ERC721URIStorage.sol";
6+
import "@openzeppelin/[email protected]/access/Ownable.sol";
7+
import "@openzeppelin/[email protected]/utils/Counters.sol";
8+
9+
contract dynNFT is ERC721, ERC721URIStorage, Ownable {
10+
using Counters for Counters.Counter;
11+
12+
Counters.Counter private _tokenIdCounter;
13+
14+
// Metadata information for each stage of the NFT on IPFS.
15+
string[] IpfsUri = [
16+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/seed.json",
17+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/purple-sprout.json",
18+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/purple-blooms.json"
19+
];
20+
21+
constructor() ERC721("dNFTs", "dNFT") {}
22+
23+
function safeMint(address to) public onlyOwner {
24+
uint256 tokenId = _tokenIdCounter.current();
25+
_tokenIdCounter.increment();
26+
_safeMint(to, tokenId);
27+
_setTokenURI(tokenId, IpfsUri[0]);
28+
}
29+
30+
function growFlower(uint256 _tokenId) public {
31+
if (flowerStage(_tokenId) >= 2) {
32+
return;
33+
}
34+
// Get the current stage of the flower and add 1
35+
uint256 newVal = flowerStage(_tokenId) + 1;
36+
// store the new URI
37+
string memory newUri = IpfsUri[newVal];
38+
// Update the URI
39+
_setTokenURI(_tokenId, newUri);
40+
}
41+
42+
// determine the stage of the flower growth
43+
function flowerStage(uint256 _tokenId) public view returns (uint256) {
44+
string memory _uri = tokenURI(_tokenId);
45+
// Seed
46+
if (compareStrings(_uri, IpfsUri[0])) {
47+
return 0;
48+
}
49+
// Sprout
50+
if (compareStrings(_uri, IpfsUri[1])) {
51+
return 1;
52+
}
53+
// Must be a Bloom
54+
return 2;
55+
}
56+
57+
/*
58+
********************
59+
* HELPER FUNCTIONS *
60+
********************
61+
*/
62+
// helper function to compare strings
63+
function compareStrings(string memory a, string memory b)
64+
public
65+
pure
66+
returns (bool)
67+
{
68+
return (keccak256(abi.encodePacked((a))) ==
69+
keccak256(abi.encodePacked((b))));
70+
}
71+
72+
// The following functions are overrides required by Solidity.
73+
74+
function _burn(uint256 tokenId)
75+
internal
76+
override(ERC721, ERC721URIStorage)
77+
{
78+
super._burn(tokenId);
79+
}
80+
81+
function tokenURI(uint256 tokenId)
82+
public
83+
view
84+
override(ERC721, ERC721URIStorage)
85+
returns (string memory)
86+
{
87+
return super.tokenURI(tokenId);
88+
}
89+
}

dynamic-nft/2_complete.sol

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
5+
import "@openzeppelin/[email protected]/token/ERC721/extensions/ERC721URIStorage.sol";
6+
import "@openzeppelin/[email protected]/access/Ownable.sol";
7+
import "@openzeppelin/[email protected]/utils/Counters.sol";
8+
9+
contract dynNFT is ERC721, ERC721URIStorage, Ownable {
10+
using Counters for Counters.Counter;
11+
12+
Counters.Counter private _tokenIdCounter;
13+
14+
// Metadata information for each stage of the NFT on IPFS.
15+
string[] IpfsUri = [
16+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/seed.json",
17+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/purple-sprout.json",
18+
"https://ipfs.io/ipfs/QmYaTsyxTDnrG4toc8721w62rL4ZBKXQTGj9c9Rpdrntou/purple-blooms.json"
19+
];
20+
21+
uint256 lastTimeStamp;
22+
uint256 interval;
23+
24+
constructor(uint256 _interval) ERC721("dNFTs", "dNFT") {
25+
interval = _interval;
26+
lastTimeStamp = block.timestamp;
27+
}
28+
29+
function checkUpkeep(
30+
bytes calldata /* checkData */
31+
)
32+
external
33+
view
34+
returns (
35+
bool upkeepNeeded,
36+
bytes memory /* performData */
37+
)
38+
{
39+
upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
40+
// We don't use the checkData in this example. The checkData is defined when the Upkeep was registered.
41+
}
42+
43+
function performUpkeep(
44+
bytes calldata /* performData */
45+
) external {
46+
//We highly recommend revalidating the upkeep in the performUpkeep function
47+
if ((block.timestamp - lastTimeStamp) > interval) {
48+
lastTimeStamp = block.timestamp;
49+
growFlower(0);
50+
}
51+
// We don't use the performData in this example. The performData is generated by the Keeper's call to your checkUpkeep function
52+
}
53+
54+
function safeMint(address to) public onlyOwner {
55+
uint256 tokenId = _tokenIdCounter.current();
56+
_tokenIdCounter.increment();
57+
_safeMint(to, tokenId);
58+
_setTokenURI(tokenId, IpfsUri[0]);
59+
}
60+
61+
function growFlower(uint256 _tokenId) public {
62+
if (flowerStage(_tokenId) >= 2) {
63+
return;
64+
}
65+
// Get the current stage of the flower and add 1
66+
uint256 newVal = flowerStage(_tokenId) + 1;
67+
// store the new URI
68+
string memory newUri = IpfsUri[newVal];
69+
// Update the URI
70+
_setTokenURI(_tokenId, newUri);
71+
}
72+
73+
// determine the stage of the flower growth
74+
function flowerStage(uint256 _tokenId) public view returns (uint256) {
75+
string memory _uri = tokenURI(_tokenId);
76+
// Seed
77+
if (compareStrings(_uri, IpfsUri[0])) {
78+
return 0;
79+
}
80+
// Sprout
81+
if (compareStrings(_uri, IpfsUri[1])) {
82+
return 1;
83+
}
84+
// Must be a Bloom
85+
return 2;
86+
}
87+
88+
/*
89+
********************
90+
* HELPER FUNCTIONS *
91+
********************
92+
*/
93+
// helper function to compare strings
94+
function compareStrings(string memory a, string memory b)
95+
public
96+
pure
97+
returns (bool)
98+
{
99+
return (keccak256(abi.encodePacked((a))) ==
100+
keccak256(abi.encodePacked((b))));
101+
}
102+
103+
// The following functions are overrides required by Solidity.
104+
105+
function _burn(uint256 tokenId)
106+
internal
107+
override(ERC721, ERC721URIStorage)
108+
{
109+
super._burn(tokenId);
110+
}
111+
112+
function tokenURI(uint256 tokenId)
113+
public
114+
view
115+
override(ERC721, ERC721URIStorage)
116+
returns (string memory)
117+
{
118+
return super.tokenURI(tokenId);
119+
}
120+
}

dynamic-nft/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Dynamic NFTs
2+
This repo will support the [Creating Dynamic NFTs](https://www.youtube.com/watch?v=E7Rm1LUKhj4) tutorial.
3+
4+
## How to Use The Repo
5+
6+
This repo consists of two files:
7+
- **1_starter.sol**: This is the starter file with the basic OpenZeppelin contract plus helper functions
8+
- **2_complete.sol**: the final contract for use with keepers
9+
10+
Both versions of the contracts can be deployed as is via a tool like [Remix](https://remix.ethereum.org/).

0 commit comments

Comments
 (0)