War veteran, Software developer, and one crazy cat on the blockchain...


Share Dialog
Share Dialog
War veteran, Software developer, and one crazy cat on the blockchain...
Four score, and seven epochs ago, I had a dream that not all smart contracts were created equal, and 0xdEaD is not the way to burn NFT’s… yes it’s a quib to a friend, but still valiant effort old friend.
So let’s begin this great ballad of the next DaemonDAO perfection shall we? This is episode 4, aka “The return of the Waves.” You might remember the developer from Episode 1 (DaemonDAO, aka “The Daemons Awaken”) and 3 (TinyDaemons aka “Attack of the Tinies”). Well I’m back with one simple quest… Make some Tiny’s disappear. I will eventually publish those rollups as well, or I won’t since the only one that matters in this scenario is the TinyDaemon project.
Project’s premise is simple, PFP + Music = new ERC721. Well hold the phone let me drop the requirements from Lucky himself…
publicMint(uint256 quant) @ 60 FTM
burnToMint(uint256[] ids) - burn 5 Tinies for 1 Wave
teamMint(uint256 quant) - 400 Team mints for holders et al
name()/symbol() - WaveDaemons/WAVEDMN
1k supply
IPFS/API mint
Images/metadata provided
Use that python magic for names/images/random
Provenance for Metadata/Images (Pseudorandom engine)
EIP 2981 - Tie to TinyDaemon contract
TimeCop settings
1671314400 (17 Dec 2022 @ 10p GMT) - Start
***NO PRESALES***
Tie to UI Provided
WaveDameons HTML/JS
Actually not difficult to write, only difficult to implement, for a few factors mainly setApprovalForAll v approve, and writing an interface just to reach the burn function for TinyDameons. So let’s kick off with round 1, and remember this is a 12 round fight. Here’s the summary of round 1.
https://testnet.ftmscan.com/address/0x35628b61395946ed69c7938120d9e08579d479ce#code
Let’s see how astute you are for these two code blocks…
function burnToMint(
uint256[] memory ids
) external
notPaused()
onlySale()
nonReentrant() {
// now to get the five per
uint256 quant = ids.length / 5;
if (quant == 0) {
revert MaxSplaining({
reason: "Main:B1"
});
} else {
for (uint x = 0; x < quant;) {
_safeMint(msg.sender, _nextUp());
_oneRegularMint();
unchecked { ++x; }
}
// must call Approve for all first
for (uint y = 0; y < quant * 5;) {
ERC721Burnable(Tinys).burn(ids[y]);
}
}
}
function teamMint(
uint256 quant
) external
onlyOwner() {
if (this.minterMinted() + quant > this.minterCapacity() &&
this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()) {
revert MaxSplaining ({
reason: "Main:TM1"
});
}
for (uint x = 0; x < quant;) {
// mint it
_mint(this.owner(), _nextUp());
_oneTeamMint();
unchecked { ++x; }
}
}
Now let’s scope the errors shall we?
Error 1 (burn to mint)
Error 2 (team mint)
Error 2 again (team mint)
So what went wrong? Let’s break it down… for starters dropped the ERC721Burnable(Tinys).burn(ids[y]); for IERC721Burnable(Tinys).burn(ids[y]); but there’s a glaring issue there in the for loop. There’s no ++y. Add a dash of unchecked { ++y; } and that remedied this Error. Also, forgot an “overflow” and fixed that as well. So now we got this…
function burnToMint(
uint256[] memory ids
) external
notPaused()
onlySale()
nonReentrant() {
// now to get the five per
uint256 quant = ids.length / 5;
if (quant == 0) {
revert MaxSplaining({
reason: "Main:B1"
});
} else if (this.minterMinted() + quant > this.minterCapacity()) {
revert MaxSplaining ({
reason: "Main:B2"
});
} else {
for (uint x = 0; x < quant;) {
_safeMint(msg.sender, _nextUp());
_oneRegularMint();
unchecked { ++x; }
}
// must call Approve for all first
for (uint y = 0; y < quant * 5;) {
IERC721Burnable(Tinys).burn(ids[y]);
unchecked { ++y; }
}
}
}
What about the second error? It all lies with this… (this.minterMinted() + quant > this.minterCapacity() && this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()). If you use && then both have to fire true to toss the revert. Simple hot fix, swap || for && and then either or can short circuit the if statement. Now we got this…
function teamMint(
uint256 quant
) external
onlyOwner() {
if (this.minterMinted() + quant > this.minterCapacity() ||
this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()) {
revert MaxSplaining ({
reason: "Main:TM1"
});
}
for (uint x = 0; x < quant;) {
// mint it
_mint(this.owner(), _nextUp());
_oneTeamMint();
unchecked { ++x; }
}
}
So after passing my unit tests like normal, here we go
ERC 721 - passed
EIP 1412 - passed (safeBatchTransferFrom())
EIP 2981 - passed
MaxAccess - passed (EIP 173-esque control with bytes4 roles)
PaymentSplitterV2 - passed
PsuedoRandom.Engine - passed
TimeCop - passed
Next comes the metadata, and you know how I usually get this… a massive dump file or I generate those pesky images and jsons (Hash-Lips). Well this is Lucky, so I at least don’t have to do that but… There’s this magic where I randomize the images (again), generate provenances, and IPFS pin those hashes. So, we break for forty minutes and upload it all, this time he didn’t kill me with gigabytes of data. Oh and don’t forget I go pull the current NFT Metadata Standards from a big source. Here’s a before and after screenshot.

Key takeaways:
Frequency added to Attributes
used in a few marketplaces like PaintSwap
provides on chain rarity
Count added to Attributes
provides on chain rarity
IPFS links
avoid directories you pin these
link to files
Image and Animation URL
This is a combo, PFP and Music
Moved from directory hash/file to file hash
Now we can talk about API vs. IPFS since this is a chainID 250 (Fantom main net) deployment, we can easily go API for free using the RPC of https://rpc.fantom.tools and some quick and hot JavaScript for this back end. Guess what the back end count is at now… two. A simple REST API via Express.JS is more than sufficient if you just need to know. On my weekly series I’ll cover this during, “NFT Weeks.” Once minted out, or we end the mint, this has happened before see the DaemonDAO mint. I’ll swap this from API to IPFS and shut down the queries to my server.

Now some of the final things to do… tie a front end to a smart contract. Knowing Lucky, and at this point of over 15 months of working together, he’s probably done a front end and didn’t close all the HTML5 tags. Do not worry, I got you, and already did the two space tabs and closed tags first… Once you know me two spaces are the key, because I split screen almost everything. I also mirror all front ends before pushing to the repositories on GitHub.com. Why? Because it’s easier to destroy it on my server and fix it, than to constantly wait for a host to upload it.
So, let’s deploy this thing…


So where are we at now… yeah about to write some web3.js for a front end in HTML5 or something. So post HTML5 cleanup and importing all the right npm’s, we can use web3.js pretty effectively. Here’s some screen caps of me chopping away at some of the forked code (from tinydaemons.js) we don’t need anymore…


This is why you are verbose in console.log and web3. You must know what your program is doing and why it’s tossing errors left and right during the, “burn it to the ground,” phase of building. like look at this sexy combo of, “how we do this…”

So after tying up some loose ends and getting all the HTML5 and web3.JS to agree then you must push it. So there was a huge change log to this but I’ll skim over the absolute details in a bit. You probably won’t get everything as a green light the first time unless you got some skills. Good news? I got some skills. So from starting this only 24h ago we got this massive checklist done…
Smart Contracts tested and deployed to testnet
NFT Metadata “reshuffled” and enhanced
IPFS Pinning of all Images/JSONs/Music
Web2 tied into Web3
Smart Contract Deployed to main net
States loaded
Approval sent to PaintSwap
So what was the main take away of this? For starters the Max-721/2022 is a well developed streamlined program that is highly modular that needs maybe one more reduction of externals to jam the full breadth in there. I’m talking about ERC-20 and “ETH” payments. I’m talking about having EIP 2981 and ContractURI in the same mix with the Psuedorandom engine and Allow Lists and basic ERC-721. Soon I’ll roll parts of the Max-721/2023 that’s UUPS into the 2022 edition to optimize it some more. 24kb is 24kb, that’s the part that sucks the worst. Stay tuned… there’s always going to be more.
Thanks for the time,
Max Flow O₂
P.S. Web Repo, Contracts Repo
Four score, and seven epochs ago, I had a dream that not all smart contracts were created equal, and 0xdEaD is not the way to burn NFT’s… yes it’s a quib to a friend, but still valiant effort old friend.
So let’s begin this great ballad of the next DaemonDAO perfection shall we? This is episode 4, aka “The return of the Waves.” You might remember the developer from Episode 1 (DaemonDAO, aka “The Daemons Awaken”) and 3 (TinyDaemons aka “Attack of the Tinies”). Well I’m back with one simple quest… Make some Tiny’s disappear. I will eventually publish those rollups as well, or I won’t since the only one that matters in this scenario is the TinyDaemon project.
Project’s premise is simple, PFP + Music = new ERC721. Well hold the phone let me drop the requirements from Lucky himself…
publicMint(uint256 quant) @ 60 FTM
burnToMint(uint256[] ids) - burn 5 Tinies for 1 Wave
teamMint(uint256 quant) - 400 Team mints for holders et al
name()/symbol() - WaveDaemons/WAVEDMN
1k supply
IPFS/API mint
Images/metadata provided
Use that python magic for names/images/random
Provenance for Metadata/Images (Pseudorandom engine)
EIP 2981 - Tie to TinyDaemon contract
TimeCop settings
1671314400 (17 Dec 2022 @ 10p GMT) - Start
***NO PRESALES***
Tie to UI Provided
WaveDameons HTML/JS
Actually not difficult to write, only difficult to implement, for a few factors mainly setApprovalForAll v approve, and writing an interface just to reach the burn function for TinyDameons. So let’s kick off with round 1, and remember this is a 12 round fight. Here’s the summary of round 1.
https://testnet.ftmscan.com/address/0x35628b61395946ed69c7938120d9e08579d479ce#code
Let’s see how astute you are for these two code blocks…
function burnToMint(
uint256[] memory ids
) external
notPaused()
onlySale()
nonReentrant() {
// now to get the five per
uint256 quant = ids.length / 5;
if (quant == 0) {
revert MaxSplaining({
reason: "Main:B1"
});
} else {
for (uint x = 0; x < quant;) {
_safeMint(msg.sender, _nextUp());
_oneRegularMint();
unchecked { ++x; }
}
// must call Approve for all first
for (uint y = 0; y < quant * 5;) {
ERC721Burnable(Tinys).burn(ids[y]);
}
}
}
function teamMint(
uint256 quant
) external
onlyOwner() {
if (this.minterMinted() + quant > this.minterCapacity() &&
this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()) {
revert MaxSplaining ({
reason: "Main:TM1"
});
}
for (uint x = 0; x < quant;) {
// mint it
_mint(this.owner(), _nextUp());
_oneTeamMint();
unchecked { ++x; }
}
}
Now let’s scope the errors shall we?
Error 1 (burn to mint)
Error 2 (team mint)
Error 2 again (team mint)
So what went wrong? Let’s break it down… for starters dropped the ERC721Burnable(Tinys).burn(ids[y]); for IERC721Burnable(Tinys).burn(ids[y]); but there’s a glaring issue there in the for loop. There’s no ++y. Add a dash of unchecked { ++y; } and that remedied this Error. Also, forgot an “overflow” and fixed that as well. So now we got this…
function burnToMint(
uint256[] memory ids
) external
notPaused()
onlySale()
nonReentrant() {
// now to get the five per
uint256 quant = ids.length / 5;
if (quant == 0) {
revert MaxSplaining({
reason: "Main:B1"
});
} else if (this.minterMinted() + quant > this.minterCapacity()) {
revert MaxSplaining ({
reason: "Main:B2"
});
} else {
for (uint x = 0; x < quant;) {
_safeMint(msg.sender, _nextUp());
_oneRegularMint();
unchecked { ++x; }
}
// must call Approve for all first
for (uint y = 0; y < quant * 5;) {
IERC721Burnable(Tinys).burn(ids[y]);
unchecked { ++y; }
}
}
}
What about the second error? It all lies with this… (this.minterMinted() + quant > this.minterCapacity() && this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()). If you use && then both have to fire true to toss the revert. Simple hot fix, swap || for && and then either or can short circuit the if statement. Now we got this…
function teamMint(
uint256 quant
) external
onlyOwner() {
if (this.minterMinted() + quant > this.minterCapacity() ||
this.minterTeamMintsMinted() + quant > this.minterTeamMintsCapacity()) {
revert MaxSplaining ({
reason: "Main:TM1"
});
}
for (uint x = 0; x < quant;) {
// mint it
_mint(this.owner(), _nextUp());
_oneTeamMint();
unchecked { ++x; }
}
}
So after passing my unit tests like normal, here we go
ERC 721 - passed
EIP 1412 - passed (safeBatchTransferFrom())
EIP 2981 - passed
MaxAccess - passed (EIP 173-esque control with bytes4 roles)
PaymentSplitterV2 - passed
PsuedoRandom.Engine - passed
TimeCop - passed
Next comes the metadata, and you know how I usually get this… a massive dump file or I generate those pesky images and jsons (Hash-Lips). Well this is Lucky, so I at least don’t have to do that but… There’s this magic where I randomize the images (again), generate provenances, and IPFS pin those hashes. So, we break for forty minutes and upload it all, this time he didn’t kill me with gigabytes of data. Oh and don’t forget I go pull the current NFT Metadata Standards from a big source. Here’s a before and after screenshot.

Key takeaways:
Frequency added to Attributes
used in a few marketplaces like PaintSwap
provides on chain rarity
Count added to Attributes
provides on chain rarity
IPFS links
avoid directories you pin these
link to files
Image and Animation URL
This is a combo, PFP and Music
Moved from directory hash/file to file hash
Now we can talk about API vs. IPFS since this is a chainID 250 (Fantom main net) deployment, we can easily go API for free using the RPC of https://rpc.fantom.tools and some quick and hot JavaScript for this back end. Guess what the back end count is at now… two. A simple REST API via Express.JS is more than sufficient if you just need to know. On my weekly series I’ll cover this during, “NFT Weeks.” Once minted out, or we end the mint, this has happened before see the DaemonDAO mint. I’ll swap this from API to IPFS and shut down the queries to my server.

Now some of the final things to do… tie a front end to a smart contract. Knowing Lucky, and at this point of over 15 months of working together, he’s probably done a front end and didn’t close all the HTML5 tags. Do not worry, I got you, and already did the two space tabs and closed tags first… Once you know me two spaces are the key, because I split screen almost everything. I also mirror all front ends before pushing to the repositories on GitHub.com. Why? Because it’s easier to destroy it on my server and fix it, than to constantly wait for a host to upload it.
So, let’s deploy this thing…


So where are we at now… yeah about to write some web3.js for a front end in HTML5 or something. So post HTML5 cleanup and importing all the right npm’s, we can use web3.js pretty effectively. Here’s some screen caps of me chopping away at some of the forked code (from tinydaemons.js) we don’t need anymore…


This is why you are verbose in console.log and web3. You must know what your program is doing and why it’s tossing errors left and right during the, “burn it to the ground,” phase of building. like look at this sexy combo of, “how we do this…”

So after tying up some loose ends and getting all the HTML5 and web3.JS to agree then you must push it. So there was a huge change log to this but I’ll skim over the absolute details in a bit. You probably won’t get everything as a green light the first time unless you got some skills. Good news? I got some skills. So from starting this only 24h ago we got this massive checklist done…
Smart Contracts tested and deployed to testnet
NFT Metadata “reshuffled” and enhanced
IPFS Pinning of all Images/JSONs/Music
Web2 tied into Web3
Smart Contract Deployed to main net
States loaded
Approval sent to PaintSwap
So what was the main take away of this? For starters the Max-721/2022 is a well developed streamlined program that is highly modular that needs maybe one more reduction of externals to jam the full breadth in there. I’m talking about ERC-20 and “ETH” payments. I’m talking about having EIP 2981 and ContractURI in the same mix with the Psuedorandom engine and Allow Lists and basic ERC-721. Soon I’ll roll parts of the Max-721/2023 that’s UUPS into the 2022 edition to optimize it some more. 24kb is 24kb, that’s the part that sucks the worst. Stay tuned… there’s always going to be more.
Thanks for the time,
Max Flow O₂
P.S. Web Repo, Contracts Repo

Subscribe to MaxFlowO₂ 终端浪人

Subscribe to MaxFlowO₂ 终端浪人
<100 subscribers
<100 subscribers
No activity yet