Commit 8948f807 authored by Kegan's avatar Kegan
Browse files

Documented notes on SKinZNFT. Added randomization seed to randomSkin.

parent b9482133
...@@ -180,21 +180,21 @@ contract SkinZNFT is ERC721Enumerable, ReentrancyGuard, Ownable { ...@@ -180,21 +180,21 @@ contract SkinZNFT is ERC721Enumerable, ReentrancyGuard, Ownable {
// NOTE: if moving from arbitrum to another blockchain, this source of randomness would need uodated! // NOTE: if moving from arbitrum to another blockchain, this source of randomness would need uodated!
// generate a random number (changes between arbitrum and ethereum mainnet!) // generate a random number (changes between arbitrum and ethereum mainnet!)
function random() internal view returns(uint256) function random(uint256 seed) internal view returns(uint256)
{ {
/*ArbSys arbos = ArbSys(address(0x64)); /*ArbSys arbos = ArbSys(address(0x64));
return uint256(keccak256(abi.encodePacked("created by kegan hollern",block.number,arbos.arbBlockNumber(), arbos.arbOSVersion())));*/ return uint256(keccak256(abi.encodePacked("created by kegan hollern",block.number, seed,arbos.arbBlockNumber(), arbos.arbOSVersion())));*/
return uint256(keccak256(abi.encodePacked("created by kegan hollern",block.number))); //code for non-arbitrum randomization return uint256(keccak256(abi.encodePacked("created by kegan hollern",block.number, seed))); //code for non-arbitrum randomization
} }
function randomSkin() internal returns (SkinDefinition memory) function randomSkin(uint256 seed) internal returns (SkinDefinition memory)
{ {
require(_unmintedSkins.length > 0, "no skins are available to be randomly generated"); require(_unmintedSkins.length > 0, "no skins are available to be randomly generated");
uint256 index = 0; uint256 index = 0;
if(_unmintedSkins.length > 1) //if only one skin type remains, no reason to run RNG and waste gas if(_unmintedSkins.length > 1) //if only one skin type remains, no reason to run RNG and waste gas
{ {
uint256 rng = random(); uint256 rng = random(seed);
index = rng % _unmintedSkins.length; index = rng % _unmintedSkins.length;
} }
string memory skin_cid = _unmintedSkins[index].image; string memory skin_cid = _unmintedSkins[index].image;
...@@ -228,7 +228,7 @@ contract SkinZNFT is ERC721Enumerable, ReentrancyGuard, Ownable { ...@@ -228,7 +228,7 @@ contract SkinZNFT is ERC721Enumerable, ReentrancyGuard, Ownable {
require(!_exists(tokenId), "token already claimed"); require(!_exists(tokenId), "token already claimed");
_nftData[tokenId] = randomSkin(); _nftData[tokenId] = randomSkin(tokenId);
assert(bytes(_nftData[tokenId].image).length > 0); assert(bytes(_nftData[tokenId].image).length > 0);
assert(bytes(_nftData[tokenId].name).length > 0); assert(bytes(_nftData[tokenId].name).length > 0);
......
# SkinZNFT.sol
Smart contract written in [[Solidity]] for the Skin-Z NFTs.
Should have the following features
- Randomized mints
- Set # of each type of Skin to mint
- All data stored in a decentralized way (on chain or IPFS)
- Deployable on [[Arbitrum]]
- 0.01 ETH mint fee
- Ability for owner to withdraw portion of locked fee directly to a wallet (arbitrum) (tax savings)
Potential features:
- chainlink VRF for randomization on mints
- ability to add new NFTs for future mints
## Randomized Minting
A key aspect here is making it very difficult for people to "engineer" their way into getting a rare NFT.
Using the ETH mainnet, the best we can do is randomize based on the block number, which isn't super secure.
### Arbitrum randomization
On arbitrum, we can access the ETH block number, the Arbitrum block number, and the Arbitrum OS contract version.This allows us to make it even more difficult to game the system.
### Randomization seeds
Using a randomization seed, we can further increase the complexity.
#### Token ID as Seed
This adds complexity in that targetting a specific TokenID runs the risk of someone not gaming the system minting that ID before you get your desired state.
#### Chainlink VRF as Seed
Using Chainlink VRF, seeds could be randomly generated by paying a fee in LINK. This would ensure that gaming the system is as near to impossible as can be achieved on a blockchain.
## Minting fees
We need charge the user a fee in order to make a profit for artists.
Those fees will be held by the NFT contract, with the owner having the ability to withdraw those fees to a target address.
Making a deposit of 0.01 directly to the contract should return funds / revert. Must call the `claim` method with a token ID.
## Token Claiming
Following the standard layed out by Loot for Adventurers. Giving the minter control over token ID is important. This allows early adopters to snag cool numbers like *420* and *69*.
Tokens should only be mintable from this `claim` method.
`claim` should require a 0.01 ETH deposit be included with the call.
## TokenURI
We should generate the token JSON data URI on chain like Loot for Adventurers does. This way, we can customize the Name field of the NFT and include the TokenID in it.
This also significantly reduces the amount of IPFS files that need pinned.
The image section should be `ipfs://<content_id_here>` linking to a PNG/SVG/JPG or other format of artwork for the specified NFT.
This IPFS data should be pinned using pinata.cloud, but it should be noted that owners of these NFTs may want to pin their content as well to ensure the artwork lasts a lifetime.
## Premint Definitions
Needing a way to store IPFS CIDs and NFT amounts, this data should be stored on chain as well. This removes any need for a centralized minter.
By storing this data in a struct like
```solidity
struct SkinData {
string image;
string name;
uint256 count;
}
```
We can define a set count of each NFT that should be minted.
Once a count reaches 0, no more of that NFT image should be created.
## Randomization optimizations
By only randomizing which type of NFT we mint, and not caring about the amount of those NFTs that remain to be minted, we can simplify our minting calculations.
For example, say we had `SkinData[]` which contained all the NFTs to mint. If we selected a random element from this array, and minted it, followed by reducing it's `count` field. We could then remove elements as their `count` hits 0, making the randomization calculation not require iterating over the entire list of skins (saving gas).
### Drawbacks
We'll likely mint all of the "rare" NFTs early, leaving only common ones to be minted by the end.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment