
How to implement a `mint` function in your NFT contract?
Here’s the simplest NFT contract:import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; contract SimplestNFT is ERC721 { function mint(uint256 tokenId) external { _safeMint(msg.sender, tokenId); } } It delegates the heavy lifting to the base contract implementation (in this case, OpenZeppelin). _safeMint is responsible for matching the ERC721 spec: it does some checks, stores data about ownership, emits events and calls callbacks. But you don’t want to allow anyone to call this function wi...

BasePaint
“This is the most fun I had onchain in a long time” — creators and users of BasePaint. Let me tell you a story about building BasePaint, a collaborative pixel art project that is unlike what you typically see in the crypto space. I’ve been writing this post for the last 100 days and did my best to make sure it’s not too boring. Buckle up and consider minting!BasePaintInspired by /r/Place & The Million Dollar HomepageI remember the first time I saw the source code for an NFT smart contract. My...

NFT project story: building and open sourcing Rings for Loot
What does it take to build an NFT project? How does it work end-to-end, from an idea to collectibles in the user's wallet? How much does it cost?Rings (for Loot)Rings (for Loot) is the first and largest 3D interpretation of an entire category in Loot. Adventurers, builders, and artists are encouraged to reference Rings (for Loot) to further expand on the imagination of Loot. The Rings project is Jeremy Goldberg's idea. Jeremy made all the stunning artwork, 0xHab helped shape tokenom...
❄️



How to implement a `mint` function in your NFT contract?
Here’s the simplest NFT contract:import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; contract SimplestNFT is ERC721 { function mint(uint256 tokenId) external { _safeMint(msg.sender, tokenId); } } It delegates the heavy lifting to the base contract implementation (in this case, OpenZeppelin). _safeMint is responsible for matching the ERC721 spec: it does some checks, stores data about ownership, emits events and calls callbacks. But you don’t want to allow anyone to call this function wi...

BasePaint
“This is the most fun I had onchain in a long time” — creators and users of BasePaint. Let me tell you a story about building BasePaint, a collaborative pixel art project that is unlike what you typically see in the crypto space. I’ve been writing this post for the last 100 days and did my best to make sure it’s not too boring. Buckle up and consider minting!BasePaintInspired by /r/Place & The Million Dollar HomepageI remember the first time I saw the source code for an NFT smart contract. My...

NFT project story: building and open sourcing Rings for Loot
What does it take to build an NFT project? How does it work end-to-end, from an idea to collectibles in the user's wallet? How much does it cost?Rings (for Loot)Rings (for Loot) is the first and largest 3D interpretation of an entire category in Loot. Adventurers, builders, and artists are encouraged to reference Rings (for Loot) to further expand on the imagination of Loot. The Rings project is Jeremy Goldberg's idea. Jeremy made all the stunning artwork, 0xHab helped shape tokenom...
❄️
Share Dialog
Share Dialog

Subscribe to w1nt3r.eth

Subscribe to w1nt3r.eth
>8.2K subscribers
>8.2K subscribers
What if there was an NFT collection that can't be bought with money, but needs to be earned with skill and dedication?
Here's a story about a project I had a ton of fun building. I hope I can share at least a small fraction of my excitement with you through this post!
Every NFT collection on Ethereum is a smart contract. But the majority of NFT projects have a very simple way of getting the collection’s items: minting them for a fee. I thought it was very underwhelming, and set to explore other ways.
I started with a few challenges that can only be completed on-chain: computing hashes, sending ether, understanding block density and time, voting, etc.
The idea completely took over me. I kept thinking about it for weeks. Finally, I set aside ~48 hours and built a collection named Runes of Ethereum.
The collection has only 5 NFTs. Anyone can claim the NFT from its current owner if they demonstrate a higher skill or dedication. Runes are following the worthy!
If you are a solidity developer (or aspiring to be one!) check out the source on Etherscan and see if you can figure out what each of them does!

function claimRuneOfPower(uint256 nonce) external {
uint256 power = uint256(
keccak256(abi.encodePacked(lastPower, nonce, msg.sender))
);
require(power > lastPower, "Not powerful enough");
lastPower = power;
_transfer(ownerOf(RUNE_OF_POWER), msg.sender, RUNE_OF_POWER);
}
To get the Rune of Power, you need to submit a "stronger" nonce. When hashed with your address it should be larger than the previous power. I also included lastPower into the hash so nobody can pre-compute several nonces in advance.

function claimRuneOfWealth() external payable {
require(msg.value > lastPrice, "Insufficient payment");
address lastClaimer = ownerOf(RUNE_OF_WEALTH);
uint256 refund = lastPrice;
uint256 gift = address(this).balance - refund;
_transfer(lastClaimer, msg.sender, RUNE_OF_WEALTH);
lastPrice = msg.value;
bool success = payable(lastClaimer).send(refund);
if (!success) {
WETH weth = WETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
weth.deposit{value: refund}();
require(weth.transfer(lastClaimer, refund), "Payment failed");
}
payable(creator).transfer(gift);
}
The Rune of Wealth goes to the one who paid more ETH than the previous person. The previous owner gets their money refunded back.
Interesting detail: the previous owner can "reject" the refund (griefing attack). So the Runes contract will fallback to wrapping and sending WETH.

function claimRuneOfTime() external {
require(block.timestamp > lastTime + cooldown, "Need to wait");
lastTime = block.timestamp;
cooldown = cooldown + cooldown / 10;
_transfer(ownerOf(RUNE_OF_TIME), msg.sender, RUNE_OF_TIME);
}
The Rune of Time is available for anyone to claim after a cooldown. The cooldown itself is growing 10% at a time, so the more times it's claimed, the longer the next owner needs to wait.
To get the Rune you have to demonstrate dedication (or the ability to wield scripts and FlashBots).

function claimRuneOfSpace() external {
require(block.basefee > lastBasefee, "Block space not dense enough");
lastBasefee = block.basefee;
_transfer(ownerOf(RUNE_OF_SPACE), msg.sender, RUNE_OF_SPACE);
}
The Rune of Space can be claimed when the network gas price goes higher than the last time. The meaning behind it is that the gas price reflects block space density. I used basefee and not gasprice. The base fee can't be (efficiently) manipulated by the claimer.
Like with the Rune of Time, you have to show dedication to get the Rune.

function nominate(address collection, uint256 tokenId, address who) external {
require(tx.origin == msg.sender, "Only humans");
require(who != address(0), "Address is 0");
require(
collection == 0x4D2BB1FDfBdd3e5aC720a4c557117daB75351bfC ||
collection == 0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7 ||
collection == 0x5180db8F5c931aaE63c74266b211F580155ecac8 ||
collection == 0x8d04a8c79cEB0889Bdd12acdF3Fa9D207eD3Ff63,
"Not from this universe"
);
require(
IERC721Ownership(collection).ownerOf(tokenId) == msg.sender,
"Not authorized"
);
address prev = nominations[collection][tokenId];
if (prev != address(0)) {
votes[prev] -= 1;
}
nominations[collection][tokenId] = who;
votes[who] += 1;
if (votes[who] > lastVotes) {
lastVotes = votes[who];
_transfer(ownerOf(RUNE_OF_INFLUENCE), who, RUNE_OF_INFLUENCE);
}
}
The Rune of Influence is awarded to the person who has the most votes from the holders of @cryptopunksnfts, @crypto_coven, @lootproject, and @blitmap. These collections are very influential in the NFT space and I wanted the owner to be worthy of their vote.
Human consensus is a very hard problem, aligning communities together is a rare and valuable skill. I agree it's not as technical as other Runes, but at the same time, it also reflects the spirit of crypto.
The NFT metadata and SVG images are stored on-chain. The game will run for as long as Ethereum is alive.
The design inspiration comes from the Runic alphabet. I picked the letters that had a meaning related to the code of each NFT item. Then traced the letters and @0xHab added his magic to make it all look really nice.

Looking back, I wish I made the contract upgradeable. After I released the initial collection I had ideas for more fun Runes. I also regret not adding a way to send the Rune owners some kind of NFT that shows that they once owned that Rune. Maybe I'll do a v2 at some point!
I had a ton of fun making each Rune. It exercised almost every bit of Solidity knowledge and wit I had at the time. Mad respect to folks who managed to snipe at least one Rune (@frolic, @z0age, @jacobdehart, @eccogrinder, @EthereumCIA, @bradleyc, @asnared, and others), you are the legends!
I hope this project inspires you to think outside the box. To push the limits. To challenge the status quo. And to have fun doing things that you love.
If you liked the project but can’t get a Rune yet, feel free to mint this post’s edition, there are only 5!
What if there was an NFT collection that can't be bought with money, but needs to be earned with skill and dedication?
Here's a story about a project I had a ton of fun building. I hope I can share at least a small fraction of my excitement with you through this post!
Every NFT collection on Ethereum is a smart contract. But the majority of NFT projects have a very simple way of getting the collection’s items: minting them for a fee. I thought it was very underwhelming, and set to explore other ways.
I started with a few challenges that can only be completed on-chain: computing hashes, sending ether, understanding block density and time, voting, etc.
The idea completely took over me. I kept thinking about it for weeks. Finally, I set aside ~48 hours and built a collection named Runes of Ethereum.
The collection has only 5 NFTs. Anyone can claim the NFT from its current owner if they demonstrate a higher skill or dedication. Runes are following the worthy!
If you are a solidity developer (or aspiring to be one!) check out the source on Etherscan and see if you can figure out what each of them does!

function claimRuneOfPower(uint256 nonce) external {
uint256 power = uint256(
keccak256(abi.encodePacked(lastPower, nonce, msg.sender))
);
require(power > lastPower, "Not powerful enough");
lastPower = power;
_transfer(ownerOf(RUNE_OF_POWER), msg.sender, RUNE_OF_POWER);
}
To get the Rune of Power, you need to submit a "stronger" nonce. When hashed with your address it should be larger than the previous power. I also included lastPower into the hash so nobody can pre-compute several nonces in advance.

function claimRuneOfWealth() external payable {
require(msg.value > lastPrice, "Insufficient payment");
address lastClaimer = ownerOf(RUNE_OF_WEALTH);
uint256 refund = lastPrice;
uint256 gift = address(this).balance - refund;
_transfer(lastClaimer, msg.sender, RUNE_OF_WEALTH);
lastPrice = msg.value;
bool success = payable(lastClaimer).send(refund);
if (!success) {
WETH weth = WETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
weth.deposit{value: refund}();
require(weth.transfer(lastClaimer, refund), "Payment failed");
}
payable(creator).transfer(gift);
}
The Rune of Wealth goes to the one who paid more ETH than the previous person. The previous owner gets their money refunded back.
Interesting detail: the previous owner can "reject" the refund (griefing attack). So the Runes contract will fallback to wrapping and sending WETH.

function claimRuneOfTime() external {
require(block.timestamp > lastTime + cooldown, "Need to wait");
lastTime = block.timestamp;
cooldown = cooldown + cooldown / 10;
_transfer(ownerOf(RUNE_OF_TIME), msg.sender, RUNE_OF_TIME);
}
The Rune of Time is available for anyone to claim after a cooldown. The cooldown itself is growing 10% at a time, so the more times it's claimed, the longer the next owner needs to wait.
To get the Rune you have to demonstrate dedication (or the ability to wield scripts and FlashBots).

function claimRuneOfSpace() external {
require(block.basefee > lastBasefee, "Block space not dense enough");
lastBasefee = block.basefee;
_transfer(ownerOf(RUNE_OF_SPACE), msg.sender, RUNE_OF_SPACE);
}
The Rune of Space can be claimed when the network gas price goes higher than the last time. The meaning behind it is that the gas price reflects block space density. I used basefee and not gasprice. The base fee can't be (efficiently) manipulated by the claimer.
Like with the Rune of Time, you have to show dedication to get the Rune.

function nominate(address collection, uint256 tokenId, address who) external {
require(tx.origin == msg.sender, "Only humans");
require(who != address(0), "Address is 0");
require(
collection == 0x4D2BB1FDfBdd3e5aC720a4c557117daB75351bfC ||
collection == 0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7 ||
collection == 0x5180db8F5c931aaE63c74266b211F580155ecac8 ||
collection == 0x8d04a8c79cEB0889Bdd12acdF3Fa9D207eD3Ff63,
"Not from this universe"
);
require(
IERC721Ownership(collection).ownerOf(tokenId) == msg.sender,
"Not authorized"
);
address prev = nominations[collection][tokenId];
if (prev != address(0)) {
votes[prev] -= 1;
}
nominations[collection][tokenId] = who;
votes[who] += 1;
if (votes[who] > lastVotes) {
lastVotes = votes[who];
_transfer(ownerOf(RUNE_OF_INFLUENCE), who, RUNE_OF_INFLUENCE);
}
}
The Rune of Influence is awarded to the person who has the most votes from the holders of @cryptopunksnfts, @crypto_coven, @lootproject, and @blitmap. These collections are very influential in the NFT space and I wanted the owner to be worthy of their vote.
Human consensus is a very hard problem, aligning communities together is a rare and valuable skill. I agree it's not as technical as other Runes, but at the same time, it also reflects the spirit of crypto.
The NFT metadata and SVG images are stored on-chain. The game will run for as long as Ethereum is alive.
The design inspiration comes from the Runic alphabet. I picked the letters that had a meaning related to the code of each NFT item. Then traced the letters and @0xHab added his magic to make it all look really nice.

Looking back, I wish I made the contract upgradeable. After I released the initial collection I had ideas for more fun Runes. I also regret not adding a way to send the Rune owners some kind of NFT that shows that they once owned that Rune. Maybe I'll do a v2 at some point!
I had a ton of fun making each Rune. It exercised almost every bit of Solidity knowledge and wit I had at the time. Mad respect to folks who managed to snipe at least one Rune (@frolic, @z0age, @jacobdehart, @eccogrinder, @EthereumCIA, @bradleyc, @asnared, and others), you are the legends!
I hope this project inspires you to think outside the box. To push the limits. To challenge the status quo. And to have fun doing things that you love.
If you liked the project but can’t get a Rune yet, feel free to mint this post’s edition, there are only 5!
No activity yet