Skip to content

Commit

Permalink
Separate Random Color NFT into It's Own Tutorial (#659)
Browse files Browse the repository at this point in the history
* Reorg Random Color NFT

* Fix typo

* Fix typo

* chore: runs yarn dedupe and enforces a consistent version resolution for coinbase cookie manager (#644)

* Return updated_at data for ocs registry (#661)

* Feat: Implement Amplitude Experiment infra (#639)

* Added Amplitude Experiments Initialization to initCCA

* create useVariant hook to pull experimental variants

* refactored useVariant to create simpler interface

* linted

* automated defaultDeploymentKey logic

* added defaultDeploymentKey for prod env

* Created ExperimentsContext for web app

* added more specificity to amplitude domains for CSP

* additional properties on eventData

* added amplitude deployment keys to constants

* deleted unused useVariant hook

* refactored experiment initialization in initCCA

* refactored Experiments context

* refactored usage of Experiments provider

* refactored ampDeploymentKey logic

* removed experiment initialization from initCCA

* restored index page to prior state

* refactored initCCA to pull constants into dedicated file

* created Experiments context in base-docs

* implemented experiments context in base-docs Root

* moved Experiments context to libs

* updated Experiments context integration in base-web

* fixed import statement

* deleted unused context in favor of shared version in libs

* implemented shared experiments context

* moved Experiments context to be innermost context provider in base-web

* removed unused imports from initCCA

* refactored Amplitude Experiments package into libs

* fixed yarn issue

* added type declaration for

* refactored window type declaration

* Add addresses for Fault Proof contracts on Sepolia L1 (#656)

Will add challenger address in separate PR once we finalize it.

* Rename Base Camp to Base Learn (#649)

* Rename Base Camp to Base Learn

* Rename Base Camp to Base Learn

* Update learn link

* change wallet type property from camel to snake case (#643)

* Update api key requirement, minor style updates (#642)

* Fix type and clarify inheritance ex (#655)

* Document Reth snapshot URLs (#651)

* feat(ecosystem): New additions to Ecosystem page (#647)

* feat(ecosystem): New additions to Ecosystem page

* chore(Ecosystem): Add image for Dynamic

* Update preparing-for-fault-proofs-on-base-sepolia.md (#633)

Update preparing-for-fault-proofs-on-base-sepolia

* Fix conflict

* fix conflict

---------

Co-authored-by: Brendan from DeFi <[email protected]>
Co-authored-by: Danyal Prout <[email protected]>
Co-authored-by: wbnns <[email protected]>
Co-authored-by: Olexandr Radovenchyk <[email protected]>

---------

Co-authored-by: Jordan Frankfurt <[email protected]>
Co-authored-by: moggr <[email protected]>
Co-authored-by: Brendan from DeFi <[email protected]>
Co-authored-by: Ian L. <[email protected]>
Co-authored-by: Danyal Prout <[email protected]>
Co-authored-by: wbnns <[email protected]>
Co-authored-by: Olexandr Radovenchyk <[email protected]>
  • Loading branch information
8 people authored Jul 24, 2024
1 parent 3f56389 commit 70c1b2f
Show file tree
Hide file tree
Showing 6 changed files with 1,081 additions and 411 deletions.
147 changes: 2 additions & 145 deletions apps/base-docs/tutorials/docs/1_thirdweb-unreal-nft-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The tutorial assumes you're comfortable with the basics of deploying an app and

## Reviewing the Contract

[Below], you can find an example of an ERC-721 NFT contract. It's an extension of the [OpenZeppelin ERC-721] implementation. When a user mints, they're granted an NFT with a random color. The metadata is fully onchain, as is the svg image. The image is a simple 1024\*1024 `rect`, with a `fill` of the randomly generated color.
In our tutorial for building a [Simple Onchain NFTs], you can find an example of an ERC-721 NFT contract. It's an extension of the [OpenZeppelin ERC-721] implementation. When a user mints, they're granted an NFT with a random color. The metadata is fully onchain, as is the svg image. The image is a simple 1024\*1024 `rect`, with a `fill` of the randomly generated color.

If the user dislikes the color, they may shuffle it and the NFT will change to a new randomly-selected color.

Expand Down Expand Up @@ -624,149 +624,6 @@ Compile, save, and close `Canvas_HUD`. Run the game. Your car will start red, bu
In this tutorial, you've learned how to set up Thirdweb's engine and use it to connect an Unreal Engine game to Base. You've also learned how to use their platform to deploy and manager your contracts. Finally, you've learned how to build game elements to allow players to collect new NFTs and use them to personalize their game items.
## Random Color NFT Contract
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
contract RandomColorNFT is ERC721 {
using EnumerableSet for EnumerableSet.UintSet;
mapping (address => EnumerableSet.UintSet) tokensOwned;
uint public counter;
mapping (uint => string) public tokenIdToColor;
error InvalidTokenId(uint tokenId);
error OnlyOwner(address);
constructor() ERC721("RandomColorNFT", "RCNFT") {
}
function mintTo(address _to) public {
counter++;
_safeMint(_to, counter);
tokenIdToColor[counter] = generateRandomColor();
}
struct TokenAndMetatdata {
uint tokenId;
string metadata;
}
function getNftsOwned(address owner) public view returns (TokenAndMetatdata[] memory) {
TokenAndMetatdata[] memory tokens = new TokenAndMetatdata[](tokensOwned[owner].length());
for (uint i = 0; i < tokensOwned[owner].length(); i++) {
uint tokenId = tokensOwned[owner].at(i);
tokens[i] = TokenAndMetatdata(tokenId, tokenURI(tokenId));
}
return tokens;
}
function shuffleColor(uint _tokenId) public {
if(_tokenId > counter) {
revert InvalidTokenId(_tokenId);
}
if(ownerOf(_tokenId) != msg.sender) {
revert OnlyOwner(msg.sender);
}
tokenIdToColor[_tokenId] = generateRandomColor();
}
function _update(address to, uint256 tokenId, address auth) internal override(ERC721) returns(address) {
// Only remove the token if it is not being minted
if (tokenId != counter){
tokensOwned[auth].remove(tokenId);
}
tokensOwned[to].add(tokenId);
return super._update(to, tokenId, auth);
}
function _baseURI() internal pure override returns (string memory) {
return "data:application/json;base64,";
}
function tokenURI(uint _tokenId) public view override returns (string memory) {
if(_tokenId > counter) {
revert InvalidTokenId(_tokenId);
}
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "',
name(),
' #: ',
Strings.toString(_tokenId),
'","description": "Random colors are pretty or boring!", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(render(_tokenId))),
'"}'
)
)
)
);
return string(abi.encodePacked(_baseURI(), json));
}
function render(uint _tokenId) public view returns (string memory) {
return string(
abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'>",
"<rect width='1024' height='1024' fill='",
tokenIdToColor[_tokenId],
"' />",
"</svg>"
)
);
}
// Function to generate a random color hex code
function generateRandomColor() public view returns (string memory) {
// Generate a pseudo-random number using block.prevrandao
uint256 randomNum = uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, msg.sender)));
// Extract RGB components from the random number
bytes memory colorBytes = new bytes(3);
colorBytes[0] = bytes1(uint8(randomNum >> 16));
colorBytes[1] = bytes1(uint8(randomNum >> 8));
colorBytes[2] = bytes1(uint8(randomNum));
// Convert RGB components to hex string
string memory colorHex = string(abi.encodePacked(
"#",
toHexDigit(uint8(colorBytes[0]) >> 4),
toHexDigit(uint8(colorBytes[0]) & 0x0f),
toHexDigit(uint8(colorBytes[1]) >> 4),
toHexDigit(uint8(colorBytes[1]) & 0x0f),
toHexDigit(uint8(colorBytes[2]) >> 4),
toHexDigit(uint8(colorBytes[2]) & 0x0f)
));
return colorHex;
}
// Helper function to convert a uint8 to a hex character
function toHexDigit(uint8 d) internal pure returns (bytes1) {
if (d < 10) {
return bytes1(uint8(bytes1('0')) + d);
} else {
return bytes1(uint8(bytes1('a')) + d - 10);
}
}
}
```

---
[Base Learn]: https://base.org/learn
Expand All @@ -777,7 +634,6 @@ contract RandomColorNFT is ERC721 {
[thirdweb]: https://thirdweb.com/
[Gaming SDK]: https://portal.thirdweb.com/solutions/gaming/overview
[Unreal Engine Quickstart]: https://portal.thirdweb.com/solutions/gaming/unreal-engine/quickstart
[Below]: #random-color-nft-contract
[contract provided below]: #random-color-nft-contract
[Engine]: https://github.com/thirdweb-dev/engine
[run it locally]: https://portal.thirdweb.com/engine/self-host
Expand All @@ -791,3 +647,4 @@ contract RandomColorNFT is ERC721 {
[thirdweb engine dashboard]: https://thirdweb.com/dashboard/engine
[wallet best practices]: https://portal.thirdweb.com/engine/features/backend-wallets#best-practices
[conversion function]: https://blueprintue.com/blueprint/vm4ujcqe/
[Simple Onchain NFTs]: /simple-onchain-nfts
Loading

0 comments on commit 70c1b2f

Please sign in to comment.